mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-17 02:25:31 +00:00
Compare commits
152 Commits
SP-1275-FE
...
bugfix/dup
Author | SHA1 | Date | |
---|---|---|---|
976d6e385a | |||
ff07e7509d | |||
17a582ab99 | |||
09fb604acc | |||
2068df173d | |||
bfc2a381d2 | |||
778257644d | |||
c8e540e938 | |||
ba20998067 | |||
75b0b24543 | |||
c03b8f290c | |||
2c684a9495 | |||
fbc45b465f | |||
c9c939c59f | |||
e1a2465130 | |||
86164e746a | |||
4a5176cf22 | |||
2bb7a6950a | |||
46860619a0 | |||
d1bb7b129f | |||
7adce3b94c | |||
91f93d4395 | |||
42d6b64e58 | |||
b11d4186fb | |||
24130be665 | |||
8c960bd5f1 | |||
fb8ccdf0a6 | |||
1975a1b6f4 | |||
367d6717e7 | |||
8c637e40ff | |||
05b3180510 | |||
177a4711fe | |||
d7a37c6519 | |||
b901791079 | |||
5d16555748 | |||
bd15f30b8a | |||
5f2684bdf4 | |||
b4ef22ef0a | |||
d8afb562eb | |||
67a164e6d2 | |||
cf20bdcd42 | |||
32b45ea5d7 | |||
62fb8b3097 | |||
065bd33511 | |||
b9ab782c01 | |||
4d5adf948c | |||
e45f57ca03 | |||
84264391d9 | |||
2e4f904d3a | |||
c46cfb48a8 | |||
a0dd128557 | |||
34fa426163 | |||
1407c173b0 | |||
9431eb79c1 | |||
70f1f39fce | |||
f912b41fd8 | |||
977875f1f2 | |||
8136804694 | |||
ce253b2034 | |||
024fbcdb83 | |||
a8430a7d3d | |||
7ef6020dd8 | |||
2a77483f46 | |||
a6fc99443b | |||
ae95d06482 | |||
18c886753d | |||
62bf4f2944 | |||
726c173a76 | |||
d538b3667e | |||
72ae3b1727 | |||
01d5cb48cc | |||
3216d6b879 | |||
52e1ff94de | |||
0cc867a4ea | |||
3de7606a00 | |||
f709b92e12 | |||
f1667d4458 | |||
b4f03ab6c3 | |||
4c38c50649 | |||
8b441aaf46 | |||
afdd44e098 | |||
fc1d394509 | |||
dce44e20ec | |||
91c4c772b5 | |||
e0be44a507 | |||
d4a7dd5854 | |||
50eb890d18 | |||
9eefd522b7 | |||
4989a0e95c | |||
3c6b9f9ef4 | |||
86b8771694 | |||
ea1d3d18c8 | |||
9044645f95 | |||
7699453e6d | |||
d1a21be983 | |||
db8e5a4aa6 | |||
fa5bb350c3 | |||
920827d763 | |||
d3902d622e | |||
a4432656ab | |||
90e0d2f52b | |||
08e5e17910 | |||
f57348e5cd | |||
be168aed93 | |||
a66784473f | |||
c0a963ded5 | |||
7945cefe53 | |||
7d0e50fb1d | |||
117f6190dd | |||
748c67fd8b | |||
1bfab8cc76 | |||
7dcaa20da1 | |||
616adccfdd | |||
abf6555485 | |||
be0533645e | |||
254e03e3c7 | |||
db1f29e2b2 | |||
dba89027e3 | |||
6bea4c2f4a | |||
e2ec986bb9 | |||
ceb1e1d23a | |||
ee12980b47 | |||
4849bb41ba | |||
ebcd89d2a5 | |||
a7bdbfe3ec | |||
db84a9aa5e | |||
1493e35f6a | |||
f19cc616be | |||
06383018b9 | |||
9e3a78f6b7 | |||
a27b2e758c | |||
1023170788 | |||
140f4ff5e2 | |||
cbaeecc968 | |||
2a95720cb0 | |||
4fae2d6be0 | |||
5b84076572 | |||
9bf37243a6 | |||
acad0e8c9c | |||
79f5ef7871 | |||
6493f02bcc | |||
c7c8898763 | |||
62ee9a72d6 | |||
55695ca5db | |||
c2f5a8df10 | |||
cd9821679e | |||
bfd3d4542e | |||
35a99ccda7 | |||
978934399e | |||
bc32fe7941 | |||
cb6d50d367 | |||
97eb1c152b |
@ -25,13 +25,13 @@ jobs:
|
|||||||
- name: Set up Flutter
|
- name: Set up Flutter
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
flutter-version: '3.22.2' # Specify the Flutter version you want to use
|
flutter-version: '3.27.3' # Specify the Flutter version you want to use
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: flutter pub get
|
run: flutter pub get
|
||||||
|
|
||||||
- name: Build Flutter Web App
|
- name: Build Flutter Web App
|
||||||
run: flutter build web --release --dart-define=FLAVOR=production
|
run: flutter build web --web-renderer canvaskit -t lib/main.dart
|
||||||
|
|
||||||
- name: Build And Deploy
|
- name: Build And Deploy
|
||||||
id: builddeploy
|
id: builddeploy
|
||||||
|
@ -25,13 +25,13 @@ jobs:
|
|||||||
- name: Set up Flutter
|
- name: Set up Flutter
|
||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
flutter-version: '3.22.2' # Specify the Flutter version you want to use
|
flutter-version: '3.27.3' # Specify the Flutter version you want to use
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: flutter pub get
|
run: flutter pub get
|
||||||
|
|
||||||
- name: Build Flutter Web App
|
- name: Build Flutter Web App
|
||||||
run: flutter build web --release --dart-define=FLAVOR=development
|
run: flutter build web --web-renderer canvaskit -t lib/main_dev.dart
|
||||||
|
|
||||||
- name: Build And Deploy
|
- name: Build And Deploy
|
||||||
id: builddeploy
|
id: builddeploy
|
||||||
|
33
.vscode/launch.json
vendored
33
.vscode/launch.json
vendored
@ -10,11 +10,12 @@
|
|||||||
"type": "dart",
|
"type": "dart",
|
||||||
|
|
||||||
"args": [
|
"args": [
|
||||||
|
"-d",
|
||||||
"--dart-define",
|
"chrome",
|
||||||
|
"--web-port",
|
||||||
"FLAVOR=development"
|
"3000",
|
||||||
|
"-t",
|
||||||
|
"lib/main_dev.dart",
|
||||||
],
|
],
|
||||||
|
|
||||||
"flutterMode": "debug"
|
"flutterMode": "debug"
|
||||||
@ -28,11 +29,12 @@
|
|||||||
"type": "dart",
|
"type": "dart",
|
||||||
|
|
||||||
"args": [
|
"args": [
|
||||||
|
"-d",
|
||||||
"--dart-define",
|
"chrome",
|
||||||
|
"--web-port",
|
||||||
"FLAVOR=staging"
|
"3000",
|
||||||
|
"-t",
|
||||||
|
"lib/main_staging.dart",
|
||||||
],
|
],
|
||||||
|
|
||||||
"flutterMode": "debug"
|
"flutterMode": "debug"
|
||||||
@ -46,11 +48,12 @@
|
|||||||
"type": "dart",
|
"type": "dart",
|
||||||
|
|
||||||
"args": [
|
"args": [
|
||||||
|
"-d",
|
||||||
"--dart-define",
|
"chrome",
|
||||||
|
"--web-port",
|
||||||
"FLAVOR=production"
|
"3000",
|
||||||
|
"-t",
|
||||||
|
"lib/main.dart",
|
||||||
],
|
],
|
||||||
|
|
||||||
"flutterMode": "debug"
|
"flutterMode": "debug"
|
||||||
|
@ -16,7 +16,12 @@ samples, guidance on mobile development, and a full API reference.
|
|||||||
|
|
||||||
## USEFUL COMMANDS
|
## USEFUL COMMANDS
|
||||||
|
|
||||||
Run on chrome: flutter run -d chrome --dart-define=FLAVOR='ENV_NAME'
|
- Building for the Web
|
||||||
|
- CanvasKit
|
||||||
|
- `flutter build web --web-renderer canvaskit -t lib/main_dev.dart --output=build/web_dev` - build for DEVELOPMENT.
|
||||||
|
- `flutter build web --web-renderer canvaskit -t lib/main_staging.dart --output=build/web_stg` - build for STAGING.
|
||||||
|
- `flutter build web --web-renderer canvaskit -t lib/main.dart --output=build/web` - build for PRODUCTION.
|
||||||
|
|
||||||
|
- run command: `flutter run -d chrome --target=lib/main_dev.dart`
|
||||||
|
|
||||||
Build: flutter build web --release --dart-define=FLAVOR='ENV_NAME'
|
|
||||||
|
|
||||||
|
4
assets/icons/device_tag_ic.svg
Normal file
4
assets/icons/device_tag_ic.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="8" height="9" viewBox="0 0 8 9" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M7.13918 0.5H4.10294C4.0408 0.5 3.98117 0.524721 3.93722 0.568668L0.251783 4.25398C-0.0839278 4.58982 -0.0839278 5.13617 0.251783 5.47188L3.02848 8.24852C3.1906 8.41064 3.40686 8.49994 3.63734 8.5H3.6374C3.86794 8.5 4.0842 8.41064 4.24638 8.24846L7.9317 4.56308C7.97565 4.51914 8.00037 4.4595 8.00037 4.39736L8.00043 1.36113C8.00037 0.886312 7.61399 0.5 7.13918 0.5ZM7.53159 4.30031L3.91488 7.91702C3.84127 7.9907 3.74269 8.03122 3.6374 8.03122C3.53205 8.03122 3.43353 7.9907 3.35992 7.91708L0.583222 5.14045C0.43026 4.98748 0.43026 4.73845 0.583222 4.58542L4.19999 0.968775H7.13918C7.35556 0.968775 7.53165 1.14481 7.53165 1.36119L7.53159 4.30031Z" fill="#999999"/>
|
||||||
|
<path d="M5.93455 1.8291C5.73782 1.8291 5.55288 1.90577 5.41377 2.04487C5.27466 2.18392 5.19806 2.36886 5.19806 2.56559C5.19806 2.76232 5.27466 2.94726 5.41377 3.08637C5.55288 3.22548 5.73782 3.30208 5.93455 3.30208C6.13121 3.30208 6.31616 3.22548 6.45527 3.08637C6.59437 2.94726 6.67098 2.76232 6.67098 2.56559C6.67098 2.36886 6.59437 2.18392 6.45533 2.04487C6.31622 1.90577 6.13128 1.8291 5.93455 1.8291ZM6.12383 2.75487C6.07329 2.80547 6.00602 2.83331 5.93455 2.83331C5.86301 2.83331 5.79581 2.80547 5.74527 2.75487C5.69467 2.70433 5.66683 2.63707 5.66683 2.56559C5.66683 2.49412 5.69467 2.42685 5.74527 2.37631C5.79581 2.32571 5.86307 2.29788 5.93455 2.29788C6.00602 2.29788 6.07323 2.32571 6.12383 2.37631C6.17443 2.42685 6.20226 2.49412 6.20226 2.56559C6.20226 2.63707 6.17437 2.70433 6.12383 2.75487Z" fill="#999999"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
26
lib/common/widgets/empty_search_result_widget.dart
Normal file
26
lib/common/widgets/empty_search_result_widget.dart
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class EmptySearchResultWidget extends StatelessWidget {
|
||||||
|
const EmptySearchResultWidget({
|
||||||
|
this.message = 'No results found',
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Text(
|
||||||
|
message,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: ColorsManager.lightGreyColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
53
lib/common/widgets/sidebar_communities_list.dart
Normal file
53
lib/common/widgets/sidebar_communities_list.dart
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class SidebarCommunitiesList extends StatelessWidget {
|
||||||
|
const SidebarCommunitiesList({
|
||||||
|
required this.communities,
|
||||||
|
required this.itemBuilder,
|
||||||
|
required this.scrollController,
|
||||||
|
required this.onScrollToEnd,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<CommunityModel> communities;
|
||||||
|
final Widget Function(BuildContext context, int index) itemBuilder;
|
||||||
|
final ScrollController scrollController;
|
||||||
|
final void Function() onScrollToEnd;
|
||||||
|
|
||||||
|
bool _onNotification(ScrollEndNotification notification) {
|
||||||
|
final hasReachedEnd = notification.metrics.extentAfter == 0;
|
||||||
|
if (hasReachedEnd) {
|
||||||
|
onScrollToEnd.call();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: SizedBox(
|
||||||
|
width: context.screenWidth * 0.5,
|
||||||
|
child: Scrollbar(
|
||||||
|
scrollbarOrientation: ScrollbarOrientation.left,
|
||||||
|
thumbVisibility: true,
|
||||||
|
controller: scrollController,
|
||||||
|
child: NotificationListener<ScrollEndNotification>(
|
||||||
|
onNotification: _onNotification,
|
||||||
|
child: ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
padding: const EdgeInsetsDirectional.only(start: 16),
|
||||||
|
itemCount: communities.length,
|
||||||
|
controller: scrollController,
|
||||||
|
itemBuilder: itemBuilder,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -16,12 +16,12 @@ import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.da
|
|||||||
import 'package:syncrow_web/services/locator.dart';
|
import 'package:syncrow_web/services/locator.dart';
|
||||||
import 'package:syncrow_web/utils/app_routes.dart';
|
import 'package:syncrow_web/utils/app_routes.dart';
|
||||||
import 'package:syncrow_web/utils/constants/routes_const.dart';
|
import 'package:syncrow_web/utils/constants/routes_const.dart';
|
||||||
|
import 'package:syncrow_web/utils/navigation_service.dart';
|
||||||
import 'package:syncrow_web/utils/theme/theme.dart';
|
import 'package:syncrow_web/utils/theme/theme.dart';
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
try {
|
try {
|
||||||
const environment =
|
const environment = String.fromEnvironment('FLAVOR', defaultValue: 'development');
|
||||||
String.fromEnvironment('FLAVOR', defaultValue: 'development');
|
|
||||||
await dotenv.load(fileName: '.env.$environment');
|
await dotenv.load(fileName: '.env.$environment');
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await Firebase.initializeApp(
|
await Firebase.initializeApp(
|
||||||
@ -33,9 +33,7 @@ Future<void> main() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
MyApp({
|
MyApp({super.key});
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
final GoRouter _router = GoRouter(
|
final GoRouter _router = GoRouter(
|
||||||
initialLocation: RoutesConst.auth,
|
initialLocation: RoutesConst.auth,
|
||||||
@ -59,8 +57,7 @@ class MyApp extends StatelessWidget {
|
|||||||
BlocProvider<CreateRoutineBloc>(
|
BlocProvider<CreateRoutineBloc>(
|
||||||
create: (context) => CreateRoutineBloc(),
|
create: (context) => CreateRoutineBloc(),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
|
||||||
create: (context) => HomeBloc()..add(const FetchUserInfo())),
|
|
||||||
BlocProvider<VisitorPasswordBloc>(
|
BlocProvider<VisitorPasswordBloc>(
|
||||||
create: (context) => VisitorPasswordBloc(),
|
create: (context) => VisitorPasswordBloc(),
|
||||||
),
|
),
|
||||||
@ -81,6 +78,8 @@ class MyApp extends StatelessWidget {
|
|||||||
PointerDeviceKind.unknown,
|
PointerDeviceKind.unknown,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
key: NavigationService.navigatorKey,
|
||||||
|
// scaffoldMessengerKey: NavigationService.snackbarKey,
|
||||||
theme: myTheme,
|
theme: myTheme,
|
||||||
routerConfig: _router,
|
routerConfig: _router,
|
||||||
));
|
));
|
||||||
|
87
lib/main_staging.dart
Normal file
87
lib/main_staging.dart
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:syncrow_web/firebase_options_prod.dart';
|
||||||
|
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
|
||||||
|
import 'package:syncrow_web/services/locator.dart';
|
||||||
|
import 'package:syncrow_web/utils/app_routes.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/routes_const.dart';
|
||||||
|
import 'package:syncrow_web/utils/navigation_service.dart';
|
||||||
|
import 'package:syncrow_web/utils/theme/theme.dart';
|
||||||
|
|
||||||
|
Future<void> main() async {
|
||||||
|
try {
|
||||||
|
const environment = String.fromEnvironment('FLAVOR', defaultValue: 'staging');
|
||||||
|
await dotenv.load(fileName: '.env.$environment');
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
await Firebase.initializeApp(
|
||||||
|
options: DefaultFirebaseOptionsStaging.currentPlatform,
|
||||||
|
);
|
||||||
|
initialSetup();
|
||||||
|
} catch (_) {}
|
||||||
|
runApp(MyApp());
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyApp extends StatelessWidget {
|
||||||
|
MyApp({super.key});
|
||||||
|
|
||||||
|
final GoRouter _router = GoRouter(
|
||||||
|
initialLocation: RoutesConst.auth,
|
||||||
|
routes: AppRoutes.getRoutes(),
|
||||||
|
redirect: (context, state) async {
|
||||||
|
String checkToken = await AuthBloc.getTokenAndValidate();
|
||||||
|
final loggedIn = checkToken == 'Success';
|
||||||
|
final goingToLogin = state.uri.toString() == RoutesConst.auth;
|
||||||
|
|
||||||
|
if (!loggedIn && !goingToLogin) return RoutesConst.auth;
|
||||||
|
if (loggedIn && goingToLogin) return RoutesConst.home;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MultiBlocProvider(
|
||||||
|
providers: [
|
||||||
|
BlocProvider<CreateRoutineBloc>(
|
||||||
|
create: (context) => CreateRoutineBloc(),
|
||||||
|
),
|
||||||
|
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
|
||||||
|
BlocProvider<VisitorPasswordBloc>(
|
||||||
|
create: (context) => VisitorPasswordBloc(),
|
||||||
|
),
|
||||||
|
BlocProvider<RoutineBloc>(
|
||||||
|
create: (context) => RoutineBloc(),
|
||||||
|
),
|
||||||
|
BlocProvider<SpaceTreeBloc>(
|
||||||
|
create: (context) => SpaceTreeBloc()..add(InitialEvent()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: MaterialApp.router(
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
scrollBehavior: const MaterialScrollBehavior().copyWith(
|
||||||
|
dragDevices: {
|
||||||
|
PointerDeviceKind.mouse,
|
||||||
|
PointerDeviceKind.touch,
|
||||||
|
PointerDeviceKind.stylus,
|
||||||
|
PointerDeviceKind.unknown,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
key: NavigationService.navigatorKey,
|
||||||
|
// scaffoldMessengerKey: NavigationService.snackbarKey,
|
||||||
|
theme: myTheme,
|
||||||
|
routerConfig: _router,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/auth/model/region_model.dart';
|
|||||||
import 'package:syncrow_web/pages/auth/model/token.dart';
|
import 'package:syncrow_web/pages/auth/model/token.dart';
|
||||||
import 'package:syncrow_web/pages/auth/model/user_model.dart';
|
import 'package:syncrow_web/pages/auth/model/user_model.dart';
|
||||||
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
||||||
|
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
||||||
import 'package:syncrow_web/services/auth_api.dart';
|
import 'package:syncrow_web/services/auth_api.dart';
|
||||||
@ -432,9 +433,13 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> logout(BuildContext context) async {
|
static Future<void> logout(BuildContext context) async {
|
||||||
final storage = FlutterSecureStorage();
|
const storage = FlutterSecureStorage();
|
||||||
ProjectManager.clearProjectUUID();
|
|
||||||
context.read<SpaceTreeBloc>().add(ClearAllData());
|
context.read<SpaceTreeBloc>().add(ClearAllData());
|
||||||
storage.deleteAll();
|
user = null;
|
||||||
|
context.read<HomeBloc>().user = null;
|
||||||
|
await Future.wait<void>([
|
||||||
|
ProjectManager.clearProjectUUID(),
|
||||||
|
storage.deleteAll(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,20 +201,17 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
!state.isButtonEnabled &&
|
!state.isButtonEnabled &&
|
||||||
state.remainingTime != 1
|
state.remainingTime != 1
|
||||||
? null
|
? null
|
||||||
: () {
|
:
|
||||||
|
() {
|
||||||
if (forgetBloc
|
if (forgetBloc
|
||||||
.forgetEmailKey.currentState!
|
.forgetEmailKey
|
||||||
.validate() ||
|
.currentState!
|
||||||
forgetBloc
|
|
||||||
.forgetRegionKey.currentState!
|
|
||||||
.validate()) {
|
.validate()) {
|
||||||
if (forgetBloc
|
forgetBloc.add(
|
||||||
.forgetRegionKey.currentState!
|
StartTimerEvent());
|
||||||
.validate()) {
|
|
||||||
forgetBloc.add(StartTimerEvent());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
child: Text(
|
child: Text(
|
||||||
'Get Code ${state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 ? "(${forgetBloc.formattedTime(state.remainingTime)}) " : ""}',
|
'Get Code ${state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 ? "(${forgetBloc.formattedTime(state.remainingTime)}) " : ""}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
|
@ -55,12 +55,12 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
|||||||
final isSmallScreen = isSmallScreenSize(context);
|
final isSmallScreen = isSmallScreenSize(context);
|
||||||
final isMediumScreen = isMediumScreenSize(context);
|
final isMediumScreen = isMediumScreenSize(context);
|
||||||
Size size = MediaQuery.of(context).size;
|
Size size = MediaQuery.of(context).size;
|
||||||
late ScrollController _scrollController;
|
late ScrollController scrollController;
|
||||||
_scrollController = ScrollController();
|
scrollController = ScrollController();
|
||||||
|
|
||||||
void _scrollToCenter() {
|
void scrollToCenter() {
|
||||||
final double middlePosition = _scrollController.position.maxScrollExtent / 2;
|
final double middlePosition = scrollController.position.maxScrollExtent / 2;
|
||||||
_scrollController.animateTo(
|
scrollController.animateTo(
|
||||||
middlePosition,
|
middlePosition,
|
||||||
duration: const Duration(seconds: 1),
|
duration: const Duration(seconds: 1),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
@ -68,7 +68,7 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
|||||||
}
|
}
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_scrollToCenter();
|
scrollToCenter();
|
||||||
});
|
});
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
@ -76,7 +76,7 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
|||||||
FirstLayer(
|
FirstLayer(
|
||||||
second: Center(
|
second: Center(
|
||||||
child: ListView(
|
child: ListView(
|
||||||
controller: _scrollController,
|
controller: scrollController,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
@ -199,7 +199,7 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
|||||||
width: size.width * 0.9,
|
width: size.width * 0.9,
|
||||||
child: DropdownButtonHideUnderline(
|
child: DropdownButtonHideUnderline(
|
||||||
child: DropdownButton2<String>(
|
child: DropdownButton2<String>(
|
||||||
style: TextStyle(color: Colors.black),
|
style: const TextStyle(color: Colors.black),
|
||||||
isExpanded: true,
|
isExpanded: true,
|
||||||
hint: Text(
|
hint: Text(
|
||||||
'Select your region/country',
|
'Select your region/country',
|
||||||
@ -336,6 +336,16 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
|||||||
obscureText: loginBloc.obscureText,
|
obscureText: loginBloc.obscureText,
|
||||||
keyboardType: TextInputType.visiblePassword,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
controller: loginBloc.loginPasswordController,
|
controller: loginBloc.loginPasswordController,
|
||||||
|
onFieldSubmitted: (value) {
|
||||||
|
if (loginBloc.loginFormKey.currentState!.validate()) {
|
||||||
|
loginBloc.add(LoginButtonPressed(
|
||||||
|
username: loginBloc.loginEmailController.text,
|
||||||
|
password: value,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
loginBloc.add(ChangeValidateEvent());
|
||||||
|
}
|
||||||
|
},
|
||||||
decoration: textBoxDecoration()!.copyWith(
|
decoration: textBoxDecoration()!.copyWith(
|
||||||
hintText: 'At least 8 characters',
|
hintText: 'At least 8 characters',
|
||||||
hintStyle: Theme.of(context)
|
hintStyle: Theme.of(context)
|
||||||
@ -393,7 +403,7 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
|||||||
Transform.scale(
|
Transform.scale(
|
||||||
scale: 1.2,
|
scale: 1.2,
|
||||||
child: Checkbox(
|
child: Checkbox(
|
||||||
fillColor: MaterialStateProperty.all<Color>(Colors.white),
|
fillColor: WidgetStateProperty.all<Color>(Colors.white),
|
||||||
activeColor: Colors.white,
|
activeColor: Colors.white,
|
||||||
value: loginBloc.isChecked,
|
value: loginBloc.isChecked,
|
||||||
checkColor: Colors.black,
|
checkColor: Colors.black,
|
||||||
|
@ -13,6 +13,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
late AcStatusModel deviceStatus;
|
late AcStatusModel deviceStatus;
|
||||||
final String deviceId;
|
final String deviceId;
|
||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
|
Timer? _countdownTimer;
|
||||||
|
|
||||||
AcBloc({required this.deviceId}) : super(AcsInitialState()) {
|
AcBloc({required this.deviceId}) : super(AcsInitialState()) {
|
||||||
on<AcFetchDeviceStatusEvent>(_onFetchAcStatus);
|
on<AcFetchDeviceStatusEvent>(_onFetchAcStatus);
|
||||||
@ -21,7 +22,16 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
on<AcBatchControlEvent>(_onAcBatchControl);
|
on<AcBatchControlEvent>(_onAcBatchControl);
|
||||||
on<AcFactoryResetEvent>(_onFactoryReset);
|
on<AcFactoryResetEvent>(_onFactoryReset);
|
||||||
on<AcStatusUpdated>(_onAcStatusUpdated);
|
on<AcStatusUpdated>(_onAcStatusUpdated);
|
||||||
|
on<OnClose>(_onClose);
|
||||||
|
on<IncreaseTimeEvent>(_handleIncreaseTime);
|
||||||
|
on<DecreaseTimeEvent>(_handleDecreaseTime);
|
||||||
|
on<UpdateTimerEvent>(_handleUpdateTimer);
|
||||||
|
on<ToggleScheduleEvent>(_handleToggleTimer);
|
||||||
|
on<ApiCountdownValueEvent>(_handleApiCountdownValue);
|
||||||
}
|
}
|
||||||
|
bool timerActive = false;
|
||||||
|
int scheduledHours = 0;
|
||||||
|
int scheduledMinutes = 0;
|
||||||
|
|
||||||
FutureOr<void> _onFetchAcStatus(
|
FutureOr<void> _onFetchAcStatus(
|
||||||
AcFetchDeviceStatusEvent event, Emitter<AcsState> emit) async {
|
AcFetchDeviceStatusEvent event, Emitter<AcsState> emit) async {
|
||||||
@ -30,8 +40,23 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
final status =
|
final status =
|
||||||
await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||||
deviceStatus = AcStatusModel.fromJson(event.deviceId, status.status);
|
deviceStatus = AcStatusModel.fromJson(event.deviceId, status.status);
|
||||||
|
if (deviceStatus.countdown1 != 0) {
|
||||||
|
// Convert API value to minutes
|
||||||
|
final totalMinutes = deviceStatus.countdown1 * 6;
|
||||||
|
scheduledHours = totalMinutes ~/ 60;
|
||||||
|
scheduledMinutes = totalMinutes % 60;
|
||||||
|
timerActive = true;
|
||||||
|
_startCountdownTimer(emit);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(ACStatusLoaded(
|
||||||
|
status: deviceStatus,
|
||||||
|
scheduledHours: scheduledHours,
|
||||||
|
scheduledMinutes: scheduledMinutes,
|
||||||
|
isTimerActive: timerActive,
|
||||||
|
));
|
||||||
|
|
||||||
_listenToChanges(event.deviceId);
|
_listenToChanges(event.deviceId);
|
||||||
emit(ACStatusLoaded(deviceStatus));
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(AcsFailedState(error: e.toString()));
|
emit(AcsFailedState(error: e.toString()));
|
||||||
}
|
}
|
||||||
@ -70,31 +95,16 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
|
|
||||||
void _onAcStatusUpdated(AcStatusUpdated event, Emitter<AcsState> emit) {
|
void _onAcStatusUpdated(AcStatusUpdated event, Emitter<AcsState> emit) {
|
||||||
deviceStatus = event.deviceStatus;
|
deviceStatus = event.deviceStatus;
|
||||||
emit(ACStatusLoaded(deviceStatus));
|
emit(ACStatusLoaded(status: deviceStatus));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Future<void> testFirebaseConnection() async {
|
|
||||||
// // Reference to a test node in your database
|
|
||||||
// final testRef = FirebaseDatabase.instance.ref("test");
|
|
||||||
|
|
||||||
// // Write a test value
|
|
||||||
// await testRef.set("Hello, Firebase!");
|
|
||||||
|
|
||||||
// // Listen for changes on the test node
|
|
||||||
// testRef.onValue.listen((DatabaseEvent event) {
|
|
||||||
// final data = event.snapshot.value;
|
|
||||||
// print("Data from Firebase: $data");
|
|
||||||
// // If you see "Hello, Firebase!" printed in your console, it means the connection works.
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
FutureOr<void> _onAcControl(
|
FutureOr<void> _onAcControl(
|
||||||
AcControlEvent event, Emitter<AcsState> emit) async {
|
AcControlEvent event, Emitter<AcsState> emit) async {
|
||||||
final oldValue = _getValueByCode(event.code);
|
final oldValue = _getValueByCode(event.code);
|
||||||
|
|
||||||
_updateLocalValue(event.code, event.value, emit);
|
_updateLocalValue(event.code, event.value, emit);
|
||||||
|
|
||||||
emit(ACStatusLoaded(deviceStatus));
|
emit(ACStatusLoaded(status: deviceStatus));
|
||||||
|
|
||||||
await _runDebounce(
|
await _runDebounce(
|
||||||
isBatch: false,
|
isBatch: false,
|
||||||
@ -151,7 +161,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
void _revertValueAndEmit(
|
void _revertValueAndEmit(
|
||||||
String deviceId, String code, dynamic oldValue, Emitter<AcsState> emit) {
|
String deviceId, String code, dynamic oldValue, Emitter<AcsState> emit) {
|
||||||
_updateLocalValue(code, oldValue, emit);
|
_updateLocalValue(code, oldValue, emit);
|
||||||
emit(ACStatusLoaded(deviceStatus));
|
emit(ACStatusLoaded(status: deviceStatus));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateLocalValue(String code, dynamic value, Emitter<AcsState> emit) {
|
void _updateLocalValue(String code, dynamic value, Emitter<AcsState> emit) {
|
||||||
@ -184,11 +194,16 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
if (value is bool) {
|
if (value is bool) {
|
||||||
deviceStatus = deviceStatus.copyWith(childLock: value);
|
deviceStatus = deviceStatus.copyWith(childLock: value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'countdown_time':
|
||||||
|
if (value is int) {
|
||||||
|
deviceStatus = deviceStatus.copyWith(countdown1: value);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
emit(ACStatusLoaded(deviceStatus));
|
emit(ACStatusLoaded(status: deviceStatus));
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamic _getValueByCode(String code) {
|
dynamic _getValueByCode(String code) {
|
||||||
@ -203,6 +218,8 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
return deviceStatus.fanSpeedsString;
|
return deviceStatus.fanSpeedsString;
|
||||||
case 'child_lock':
|
case 'child_lock':
|
||||||
return deviceStatus.childLock;
|
return deviceStatus.childLock;
|
||||||
|
case 'countdown_time':
|
||||||
|
return deviceStatus.countdown1;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -216,7 +233,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
||||||
deviceStatus =
|
deviceStatus =
|
||||||
AcStatusModel.fromJson(event.devicesIds.first, status.status);
|
AcStatusModel.fromJson(event.devicesIds.first, status.status);
|
||||||
emit(ACStatusLoaded(deviceStatus));
|
emit(ACStatusLoaded(status: deviceStatus));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(AcsFailedState(error: e.toString()));
|
emit(AcsFailedState(error: e.toString()));
|
||||||
}
|
}
|
||||||
@ -228,7 +245,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
|
|
||||||
_updateLocalValue(event.code, event.value, emit);
|
_updateLocalValue(event.code, event.value, emit);
|
||||||
|
|
||||||
emit(ACStatusLoaded(deviceStatus));
|
emit(ACStatusLoaded(status: deviceStatus));
|
||||||
|
|
||||||
await _runDebounce(
|
await _runDebounce(
|
||||||
isBatch: true,
|
isBatch: true,
|
||||||
@ -257,4 +274,144 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
emit(AcsFailedState(error: e.toString()));
|
emit(AcsFailedState(error: e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onClose(OnClose event, Emitter<AcsState> emit) {
|
||||||
|
_countdownTimer?.cancel();
|
||||||
|
_timer?.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleIncreaseTime(IncreaseTimeEvent event, Emitter<AcsState> emit) {
|
||||||
|
if (state is! ACStatusLoaded) return;
|
||||||
|
final currentState = state as ACStatusLoaded;
|
||||||
|
int newHours = scheduledHours;
|
||||||
|
int newMinutes = scheduledMinutes + 30;
|
||||||
|
newHours += newMinutes ~/ 60;
|
||||||
|
newMinutes = newMinutes % 60;
|
||||||
|
if (newHours > 23) {
|
||||||
|
newHours = 23;
|
||||||
|
newMinutes = 59;
|
||||||
|
}
|
||||||
|
scheduledHours = newHours;
|
||||||
|
scheduledMinutes = newMinutes;
|
||||||
|
|
||||||
|
emit(currentState.copyWith(
|
||||||
|
scheduledHours: scheduledHours,
|
||||||
|
scheduledMinutes: scheduledMinutes,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleDecreaseTime(DecreaseTimeEvent event, Emitter<AcsState> emit) {
|
||||||
|
if (state is! ACStatusLoaded) return;
|
||||||
|
final currentState = state as ACStatusLoaded;
|
||||||
|
int totalMinutes = (scheduledHours * 60) + scheduledMinutes;
|
||||||
|
totalMinutes = (totalMinutes - 30).clamp(0, 1440);
|
||||||
|
scheduledHours = totalMinutes ~/ 60;
|
||||||
|
scheduledMinutes = totalMinutes % 60;
|
||||||
|
|
||||||
|
emit(currentState.copyWith(
|
||||||
|
scheduledHours: scheduledHours,
|
||||||
|
scheduledMinutes: scheduledMinutes,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _handleToggleTimer(
|
||||||
|
ToggleScheduleEvent event, Emitter<AcsState> emit) async {
|
||||||
|
if (state is! ACStatusLoaded) return;
|
||||||
|
final currentState = state as ACStatusLoaded;
|
||||||
|
|
||||||
|
timerActive = !timerActive;
|
||||||
|
|
||||||
|
if (timerActive) {
|
||||||
|
final totalMinutes = scheduledHours * 60 + scheduledMinutes;
|
||||||
|
if (totalMinutes <= 0) {
|
||||||
|
timerActive = false;
|
||||||
|
emit(currentState.copyWith(isTimerActive: timerActive));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final scaledValue = totalMinutes ~/ 6;
|
||||||
|
await _runDebounce(
|
||||||
|
isBatch: false,
|
||||||
|
deviceId: deviceId,
|
||||||
|
code: 'countdown_time',
|
||||||
|
value: scaledValue,
|
||||||
|
oldValue: scaledValue,
|
||||||
|
emit: emit,
|
||||||
|
);
|
||||||
|
_startCountdownTimer(emit);
|
||||||
|
emit(currentState.copyWith(isTimerActive: timerActive));
|
||||||
|
} catch (e) {
|
||||||
|
timerActive = false;
|
||||||
|
emit(AcsFailedState(error: e.toString()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await _runDebounce(
|
||||||
|
isBatch: false,
|
||||||
|
deviceId: deviceId,
|
||||||
|
code: 'countdown_time',
|
||||||
|
value: 0,
|
||||||
|
oldValue: 0,
|
||||||
|
emit: emit,
|
||||||
|
);
|
||||||
|
_countdownTimer?.cancel();
|
||||||
|
scheduledHours = 0;
|
||||||
|
scheduledMinutes = 0;
|
||||||
|
emit(currentState.copyWith(
|
||||||
|
isTimerActive: timerActive,
|
||||||
|
scheduledHours: 0,
|
||||||
|
scheduledMinutes: 0,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _startCountdownTimer(Emitter<AcsState> emit) {
|
||||||
|
_countdownTimer?.cancel();
|
||||||
|
int totalSeconds = (scheduledHours * 3600) + (scheduledMinutes * 60);
|
||||||
|
|
||||||
|
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||||
|
if (totalSeconds > 0) {
|
||||||
|
totalSeconds--;
|
||||||
|
scheduledHours = totalSeconds ~/ 3600;
|
||||||
|
scheduledMinutes = (totalSeconds % 3600) ~/ 60;
|
||||||
|
add(UpdateTimerEvent());
|
||||||
|
} else {
|
||||||
|
_countdownTimer?.cancel();
|
||||||
|
timerActive = false;
|
||||||
|
scheduledHours = 0;
|
||||||
|
scheduledMinutes = 0;
|
||||||
|
add(TimerCompletedEvent());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleUpdateTimer(UpdateTimerEvent event, Emitter<AcsState> emit) {
|
||||||
|
if (state is ACStatusLoaded) {
|
||||||
|
final currentState = state as ACStatusLoaded;
|
||||||
|
emit(currentState.copyWith(
|
||||||
|
scheduledHours: scheduledHours,
|
||||||
|
scheduledMinutes: scheduledMinutes,
|
||||||
|
isTimerActive: timerActive,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleApiCountdownValue(
|
||||||
|
ApiCountdownValueEvent event, Emitter<AcsState> emit) {
|
||||||
|
if (state is ACStatusLoaded) {
|
||||||
|
final totalMinutes = event.apiValue * 6;
|
||||||
|
final scheduledHours = totalMinutes ~/ 60;
|
||||||
|
scheduledMinutes = totalMinutes % 60;
|
||||||
|
_startCountdownTimer(
|
||||||
|
emit,
|
||||||
|
);
|
||||||
|
add(UpdateTimerEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
add(OnClose());
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ sealed class AcsEvent extends Equatable {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
class AcUpdated extends AcsEvent {}
|
class AcUpdated extends AcsEvent {}
|
||||||
|
|
||||||
class AcFetchDeviceStatusEvent extends AcsEvent {
|
class AcFetchDeviceStatusEvent extends AcsEvent {
|
||||||
@ -18,10 +19,12 @@ class AcFetchDeviceStatusEvent extends AcsEvent {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [deviceId];
|
List<Object> get props => [deviceId];
|
||||||
}
|
}
|
||||||
|
|
||||||
class AcStatusUpdated extends AcsEvent {
|
class AcStatusUpdated extends AcsEvent {
|
||||||
final AcStatusModel deviceStatus;
|
final AcStatusModel deviceStatus;
|
||||||
AcStatusUpdated(this.deviceStatus);
|
AcStatusUpdated(this.deviceStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
class AcFetchBatchStatusEvent extends AcsEvent {
|
class AcFetchBatchStatusEvent extends AcsEvent {
|
||||||
final List<String> devicesIds;
|
final List<String> devicesIds;
|
||||||
|
|
||||||
@ -73,3 +76,30 @@ class AcFactoryResetEvent extends AcsEvent {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [deviceId, factoryResetModel];
|
List<Object> get props => [deviceId, factoryResetModel];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class OnClose extends AcsEvent {}
|
||||||
|
|
||||||
|
class IncreaseTimeEvent extends AcsEvent {
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class DecreaseTimeEvent extends AcsEvent {
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class ToggleScheduleEvent extends AcsEvent {}
|
||||||
|
|
||||||
|
class TimerCompletedEvent extends AcsEvent {}
|
||||||
|
|
||||||
|
class UpdateTimerEvent extends AcsEvent {
|
||||||
|
}
|
||||||
|
|
||||||
|
class ApiCountdownValueEvent extends AcsEvent {
|
||||||
|
final int apiValue;
|
||||||
|
|
||||||
|
const ApiCountdownValueEvent(this.apiValue);
|
||||||
|
}
|
||||||
|
@ -2,8 +2,9 @@ import 'package:equatable/equatable.dart';
|
|||||||
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
|
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
|
||||||
|
|
||||||
abstract class AcsState extends Equatable {
|
abstract class AcsState extends Equatable {
|
||||||
const AcsState();
|
final bool isTimerActive;
|
||||||
|
|
||||||
|
const AcsState({this.isTimerActive = false});
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
@ -15,8 +16,30 @@ class AcsLoadingState extends AcsState {}
|
|||||||
class ACStatusLoaded extends AcsState {
|
class ACStatusLoaded extends AcsState {
|
||||||
final AcStatusModel status;
|
final AcStatusModel status;
|
||||||
final DateTime timestamp;
|
final DateTime timestamp;
|
||||||
|
final int scheduledHours;
|
||||||
|
final int scheduledMinutes;
|
||||||
|
final bool isTimerActive;
|
||||||
|
|
||||||
ACStatusLoaded(this.status) : timestamp = DateTime.now();
|
ACStatusLoaded({
|
||||||
|
required this.status,
|
||||||
|
this.scheduledHours = 0,
|
||||||
|
this.scheduledMinutes = 0,
|
||||||
|
this.isTimerActive = false,
|
||||||
|
}) : timestamp = DateTime.now();
|
||||||
|
ACStatusLoaded copyWith({
|
||||||
|
AcStatusModel? status,
|
||||||
|
int? scheduledHours,
|
||||||
|
int? scheduledMinutes,
|
||||||
|
bool? isTimerActive,
|
||||||
|
int? remainingTime,
|
||||||
|
}) {
|
||||||
|
return ACStatusLoaded(
|
||||||
|
status: status ?? this.status,
|
||||||
|
scheduledHours: scheduledHours ?? this.scheduledHours,
|
||||||
|
scheduledMinutes: scheduledMinutes ?? this.scheduledMinutes,
|
||||||
|
isTimerActive: isTimerActive ?? this.isTimerActive,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [status, timestamp];
|
List<Object> get props => [status, timestamp];
|
||||||
@ -40,3 +63,14 @@ class AcsFailedState extends AcsState {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [error];
|
List<Object> get props => [error];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TimerRunInProgress extends AcsState {
|
||||||
|
final int remainingTime;
|
||||||
|
|
||||||
|
const TimerRunInProgress(this.remainingTime);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [remainingTime];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ class AcStatusModel {
|
|||||||
final bool childLock;
|
final bool childLock;
|
||||||
final TempModes acMode;
|
final TempModes acMode;
|
||||||
final FanSpeeds acFanSpeed;
|
final FanSpeeds acFanSpeed;
|
||||||
|
final int countdown1;
|
||||||
|
|
||||||
AcStatusModel({
|
AcStatusModel({
|
||||||
required this.uuid,
|
required this.uuid,
|
||||||
@ -18,6 +19,7 @@ class AcStatusModel {
|
|||||||
required this.modeString,
|
required this.modeString,
|
||||||
required this.tempSet,
|
required this.tempSet,
|
||||||
required this.currentTemp,
|
required this.currentTemp,
|
||||||
|
required this.countdown1,
|
||||||
required this.fanSpeedsString,
|
required this.fanSpeedsString,
|
||||||
required this.childLock,
|
required this.childLock,
|
||||||
}) : acMode = getACMode(modeString),
|
}) : acMode = getACMode(modeString),
|
||||||
@ -30,6 +32,7 @@ class AcStatusModel {
|
|||||||
late int currentTemp;
|
late int currentTemp;
|
||||||
late String fanSpeeds;
|
late String fanSpeeds;
|
||||||
late bool childLock;
|
late bool childLock;
|
||||||
|
late int _countdown1 = 0;
|
||||||
|
|
||||||
for (var status in jsonList) {
|
for (var status in jsonList) {
|
||||||
switch (status.code) {
|
switch (status.code) {
|
||||||
@ -51,6 +54,9 @@ class AcStatusModel {
|
|||||||
case 'child_lock':
|
case 'child_lock':
|
||||||
childLock = status.value ?? false;
|
childLock = status.value ?? false;
|
||||||
break;
|
break;
|
||||||
|
case 'countdown_time':
|
||||||
|
_countdown1 = status.value ?? 0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,6 +68,7 @@ class AcStatusModel {
|
|||||||
currentTemp: currentTemp,
|
currentTemp: currentTemp,
|
||||||
fanSpeedsString: fanSpeeds,
|
fanSpeedsString: fanSpeeds,
|
||||||
childLock: childLock,
|
childLock: childLock,
|
||||||
|
countdown1: _countdown1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +80,7 @@ class AcStatusModel {
|
|||||||
int? currentTemp,
|
int? currentTemp,
|
||||||
String? fanSpeedsString,
|
String? fanSpeedsString,
|
||||||
bool? childLock,
|
bool? childLock,
|
||||||
|
int? countdown1,
|
||||||
}) {
|
}) {
|
||||||
return AcStatusModel(
|
return AcStatusModel(
|
||||||
uuid: uuid ?? this.uuid,
|
uuid: uuid ?? this.uuid,
|
||||||
@ -82,6 +90,7 @@ class AcStatusModel {
|
|||||||
currentTemp: currentTemp ?? this.currentTemp,
|
currentTemp: currentTemp ?? this.currentTemp,
|
||||||
fanSpeedsString: fanSpeedsString ?? this.fanSpeedsString,
|
fanSpeedsString: fanSpeedsString ?? this.fanSpeedsString,
|
||||||
childLock: childLock ?? this.childLock,
|
childLock: childLock ?? this.childLock,
|
||||||
|
countdown1: countdown1 ?? this.countdown1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
|
|||||||
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
|
||||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
|
class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
|
||||||
@ -23,11 +22,13 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
final isExtraLarge = isExtraLargeScreenSize(context);
|
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||||
final isLarge = isLargeScreenSize(context);
|
final isLarge = isLargeScreenSize(context);
|
||||||
final isMedium = isMediumScreenSize(context);
|
final isMedium = isMediumScreenSize(context);
|
||||||
|
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => AcBloc(deviceId: device.uuid!)
|
create: (context) => AcBloc(deviceId: device.uuid!)
|
||||||
..add(AcFetchDeviceStatusEvent(device.uuid!)),
|
..add(AcFetchDeviceStatusEvent(device.uuid!)),
|
||||||
child: BlocBuilder<AcBloc, AcsState>(
|
child: BlocBuilder<AcBloc, AcsState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
|
final acBloc = BlocProvider.of<AcBloc>(context);
|
||||||
if (state is ACStatusLoaded) {
|
if (state is ACStatusLoaded) {
|
||||||
return GridView(
|
return GridView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||||
@ -78,56 +79,101 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
),
|
),
|
||||||
ToggleWidget(
|
ToggleWidget(
|
||||||
label: '',
|
label: '',
|
||||||
labelWidget: Row(
|
labelWidget: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
decoration: const ShapeDecoration(
|
||||||
|
color: ColorsManager.primaryColor,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: SizedBox(
|
||||||
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
padding: const EdgeInsets.all(0),
|
onPressed: () {
|
||||||
onPressed: () {},
|
if (acBloc.timerActive == false) {
|
||||||
icon: const Icon(
|
context
|
||||||
Icons.remove,
|
.read<AcBloc>()
|
||||||
size: 28,
|
.add(DecreaseTimeEvent());
|
||||||
color: ColorsManager.greyColor,
|
}
|
||||||
),
|
},
|
||||||
|
icon: const Icon(Icons.remove,
|
||||||
|
color: ColorsManager.greyColor),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'06',
|
acBloc.scheduledHours
|
||||||
style: context.textTheme.titleLarge!.copyWith(
|
.toString()
|
||||||
|
.padLeft(2, '0'),
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleLarge!
|
||||||
|
.copyWith(
|
||||||
color: ColorsManager.dialogBlueTitle,
|
color: ColorsManager.dialogBlueTitle,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'h',
|
'h',
|
||||||
style: context.textTheme.bodySmall!
|
style: Theme.of(context)
|
||||||
.copyWith(color: ColorsManager.blackColor),
|
.textTheme
|
||||||
|
.bodySmall!
|
||||||
|
.copyWith(
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'30',
|
acBloc.scheduledMinutes
|
||||||
style: context.textTheme.titleLarge!.copyWith(
|
.toString()
|
||||||
|
.padLeft(2, '0'),
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.titleLarge!
|
||||||
|
.copyWith(
|
||||||
color: ColorsManager.dialogBlueTitle,
|
color: ColorsManager.dialogBlueTitle,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text('m',
|
Text(
|
||||||
style: context.textTheme.bodySmall!
|
'm',
|
||||||
.copyWith(color: ColorsManager.blackColor)),
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodySmall!
|
||||||
|
.copyWith(
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
padding: const EdgeInsets.all(0),
|
onPressed: () {
|
||||||
onPressed: () {},
|
if (acBloc.timerActive == false) {
|
||||||
icon: const Icon(
|
context
|
||||||
Icons.add,
|
.read<AcBloc>()
|
||||||
size: 28,
|
.add(IncreaseTimeEvent());
|
||||||
color: ColorsManager.greyColor,
|
}
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.add,
|
||||||
|
color: ColorsManager.greyColor),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
value: false,
|
value: acBloc.timerActive,
|
||||||
code: 'ac_schedule',
|
code: 'ac_schedule',
|
||||||
deviceId: device.uuid!,
|
deviceId: device.uuid!,
|
||||||
icon: Assets.acSchedule,
|
icon: Assets.acSchedule,
|
||||||
onChange: (value) {},
|
onChange: (value) {
|
||||||
|
context.read<AcBloc>().add(ToggleScheduleEvent());
|
||||||
|
},
|
||||||
),
|
),
|
||||||
ToggleWidget(
|
ToggleWidget(
|
||||||
deviceId: device.uuid!,
|
deviceId: device.uuid!,
|
||||||
|
@ -60,7 +60,15 @@ class _CurrentTempState extends State<CurrentTemp> {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(CurrentTemp oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (oldWidget.tempSet != widget.tempSet) {
|
||||||
|
setState(() {
|
||||||
|
_adjustedValue = _initialAdjustedValue(widget.tempSet);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_debounce?.cancel();
|
_debounce?.cancel();
|
||||||
|
@ -9,6 +9,8 @@ import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_batch_st
|
|||||||
import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_status_view.dart';
|
import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_status_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_batch_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_batch_control_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_control_view.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_batch_control_view.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_batch_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_batch_control_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_control_view.dart';
|
import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_control_view.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_batch_control.dart';
|
import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_batch_control.dart';
|
||||||
@ -104,6 +106,9 @@ mixin RouteControlsBasedCode {
|
|||||||
);
|
);
|
||||||
case 'SOS':
|
case 'SOS':
|
||||||
return SosDeviceControlsView(device: device);
|
return SosDeviceControlsView(device: device);
|
||||||
|
|
||||||
|
case 'NCPS':
|
||||||
|
return FlushMountedPresenceSensorControlView(device: device);
|
||||||
default:
|
default:
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
@ -194,6 +199,10 @@ mixin RouteControlsBasedCode {
|
|||||||
return SOSBatchControlView(
|
return SOSBatchControlView(
|
||||||
deviceIds: devices.where((e) => (e.productType == 'SOS')).map((e) => e.uuid!).toList(),
|
deviceIds: devices.where((e) => (e.productType == 'SOS')).map((e) => e.uuid!).toList(),
|
||||||
);
|
);
|
||||||
|
case 'NCPS':
|
||||||
|
return FlushMountedPresenceSensorBatchControlView(
|
||||||
|
devicesIds: devices.where((e) => (e.productType == 'NCPS')).map((e) => e.uuid!).toList(),
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
class DeviceSubSpace {
|
||||||
|
String? id;
|
||||||
|
String? createdAt;
|
||||||
|
String? updatedAt;
|
||||||
|
String? subspaceName;
|
||||||
|
bool? disabled;
|
||||||
|
|
||||||
|
DeviceSubSpace({this.id, this.createdAt, this.updatedAt, this.subspaceName, this.disabled});
|
||||||
|
|
||||||
|
DeviceSubSpace.fromJson(Map<String, dynamic> json) {
|
||||||
|
id = json['uuid']?.toString() ?? '';
|
||||||
|
createdAt = json['createdAt']?.toString() ?? '';
|
||||||
|
updatedAt = json['updatedAt']?.toString() ?? '';
|
||||||
|
subspaceName = json['subspaceName']?.toString() ?? '';
|
||||||
|
subspaceName = json['subspaceName']?.toString() ?? '';
|
||||||
|
disabled = json['disabled'] ?? false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
class DeviceTagModel {
|
||||||
|
String? id;
|
||||||
|
String? createdAt;
|
||||||
|
String? updatedAt;
|
||||||
|
String? name;
|
||||||
|
|
||||||
|
DeviceTagModel({
|
||||||
|
this.id,
|
||||||
|
this.createdAt,
|
||||||
|
this.updatedAt,
|
||||||
|
this.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
DeviceTagModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
id = json['uuid']?.toString() ?? '';
|
||||||
|
createdAt = json['createdAt']?.toString() ?? '';
|
||||||
|
updatedAt = json['updatedAt']?.toString() ?? '';
|
||||||
|
name = json['name']?.toString() ?? '';
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_community.model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_community.model.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_space_model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_space_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_sub_space.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_subspace.model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_subspace.model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_tag_model.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/room.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/room.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
|
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
|
||||||
@ -79,9 +81,11 @@ class AllDevicesModel {
|
|||||||
int? batteryLevel;
|
int? batteryLevel;
|
||||||
String? productName;
|
String? productName;
|
||||||
List<DeviceSpaceModel>? spaces;
|
List<DeviceSpaceModel>? spaces;
|
||||||
|
List<DeviceTagModel>? deviceTags;
|
||||||
|
DeviceSubSpace? deviceSubSpace;
|
||||||
|
|
||||||
AllDevicesModel({
|
AllDevicesModel(
|
||||||
this.room,
|
{this.room,
|
||||||
this.subspace,
|
this.subspace,
|
||||||
this.unit,
|
this.unit,
|
||||||
this.community,
|
this.community,
|
||||||
@ -110,7 +114,8 @@ class AllDevicesModel {
|
|||||||
this.batteryLevel,
|
this.batteryLevel,
|
||||||
this.productName,
|
this.productName,
|
||||||
this.spaces,
|
this.spaces,
|
||||||
});
|
this.deviceTags,
|
||||||
|
this.deviceSubSpace});
|
||||||
|
|
||||||
AllDevicesModel.fromJson(Map<String, dynamic> json) {
|
AllDevicesModel.fromJson(Map<String, dynamic> json) {
|
||||||
room = (json['room'] != null && (json['room'] is Map))
|
room = (json['room'] != null && (json['room'] is Map))
|
||||||
@ -148,12 +153,15 @@ class AllDevicesModel {
|
|||||||
updateTime = int.tryParse(json['updateTime']?.toString() ?? '');
|
updateTime = int.tryParse(json['updateTime']?.toString() ?? '');
|
||||||
uuid = json['uuid']?.toString();
|
uuid = json['uuid']?.toString();
|
||||||
batteryLevel = int.tryParse(json['battery']?.toString() ?? '');
|
batteryLevel = int.tryParse(json['battery']?.toString() ?? '');
|
||||||
|
|
||||||
productName = json['productName']?.toString();
|
productName = json['productName']?.toString();
|
||||||
|
deviceTags = json['deviceTag'] != null && json['deviceTag'] is List
|
||||||
|
? (json['deviceTag'] as List).map((tag) => DeviceTagModel.fromJson(tag)).toList()
|
||||||
|
: [];
|
||||||
|
deviceSubSpace = json['subspace'] != null
|
||||||
|
? DeviceSubSpace.fromJson(json['subspace'])
|
||||||
|
: DeviceSubSpace(subspaceName: '');
|
||||||
if (json['spaces'] != null && json['spaces'] is List) {
|
if (json['spaces'] != null && json['spaces'] is List) {
|
||||||
spaces = (json['spaces'] as List)
|
spaces = (json['spaces'] as List).map((space) => DeviceSpaceModel.fromJson(space)).toList();
|
||||||
.map((space) => DeviceSpaceModel.fromJson(space))
|
|
||||||
.toList();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,8 +209,7 @@ SOS
|
|||||||
String tempIcon = '';
|
String tempIcon = '';
|
||||||
if (type == DeviceType.LightBulb) {
|
if (type == DeviceType.LightBulb) {
|
||||||
tempIcon = Assets.lightBulb;
|
tempIcon = Assets.lightBulb;
|
||||||
} else if (type == DeviceType.CeilingSensor ||
|
} else if (type == DeviceType.CeilingSensor || type == DeviceType.WallSensor) {
|
||||||
type == DeviceType.WallSensor) {
|
|
||||||
tempIcon = Assets.sensors;
|
tempIcon = Assets.sensors;
|
||||||
} else if (type == DeviceType.AC) {
|
} else if (type == DeviceType.AC) {
|
||||||
tempIcon = Assets.ac;
|
tempIcon = Assets.ac;
|
||||||
@ -232,8 +239,6 @@ SOS
|
|||||||
// tempIcon = Assets.gang3touch;
|
// tempIcon = Assets.gang3touch;
|
||||||
} else if (type == DeviceType.WaterLeak) {
|
} else if (type == DeviceType.WaterLeak) {
|
||||||
tempIcon = Assets.waterLeakNormal;
|
tempIcon = Assets.waterLeakNormal;
|
||||||
} else if (type == DeviceType.WaterLeak) {
|
|
||||||
tempIcon = Assets.waterLeakNormal;
|
|
||||||
} else {
|
} else {
|
||||||
tempIcon = Assets.logoHorizontal;
|
tempIcon = Assets.logoHorizontal;
|
||||||
}
|
}
|
||||||
@ -249,76 +254,51 @@ SOS
|
|||||||
switch (productType) {
|
switch (productType) {
|
||||||
case 'AC':
|
case 'AC':
|
||||||
return [
|
return [
|
||||||
SwitchFunction(
|
SwitchFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
ModeFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||||
ModeFunction(
|
TempSetFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
CurrentTempFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||||
TempSetFunction(
|
LevelFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
ChildLockFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||||
CurrentTempFunction(
|
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
|
||||||
LevelFunction(
|
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
|
||||||
ChildLockFunction(
|
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
case '1G':
|
case '1G':
|
||||||
return [
|
return [
|
||||||
OneGangSwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
|
OneGangSwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
|
||||||
OneGangCountdownFunction(
|
OneGangCountdownFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
|
||||||
deviceId: uuid ?? '', deviceName: name ?? ''),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
case '2G':
|
case '2G':
|
||||||
return [
|
return [
|
||||||
TwoGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
|
TwoGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
|
||||||
TwoGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
|
TwoGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
|
||||||
TwoGangCountdown1Function(
|
TwoGangCountdown1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
|
||||||
deviceId: uuid ?? '', deviceName: name ?? ''),
|
TwoGangCountdown2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
|
||||||
TwoGangCountdown2Function(
|
|
||||||
deviceId: uuid ?? '', deviceName: name ?? ''),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
case '3G':
|
case '3G':
|
||||||
return [
|
return [
|
||||||
ThreeGangSwitch1Function(
|
ThreeGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
ThreeGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||||
ThreeGangSwitch2Function(
|
ThreeGangSwitch3Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
ThreeGangCountdown1Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||||
ThreeGangSwitch3Function(
|
ThreeGangCountdown2Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
ThreeGangCountdown3Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||||
ThreeGangCountdown1Function(
|
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
|
||||||
ThreeGangCountdown2Function(
|
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
|
||||||
ThreeGangCountdown3Function(
|
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
|
||||||
];
|
];
|
||||||
case 'WPS':
|
case 'WPS':
|
||||||
return [
|
return [
|
||||||
//IF Functions
|
//IF Functions
|
||||||
PresenceStateFunction(
|
PresenceStateFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
CurrentDistanceFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||||
CurrentDistanceFunction(
|
IlluminanceValueFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
PresenceTimeFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||||
IlluminanceValueFunction(
|
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
|
||||||
PresenceTimeFunction(
|
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
|
||||||
|
|
||||||
//THEN Functions
|
//THEN Functions
|
||||||
FarDetectionFunction(
|
FarDetectionFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
MotionSensitivityFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
||||||
MotionSensitivityFunction(
|
MotionLessSensitivityFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
IndicatorFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||||
MotionLessSensitivityFunction(
|
NoOneTimeFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
|
||||||
IndicatorFunction(
|
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
|
||||||
NoOneTimeFunction(
|
|
||||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
|
||||||
|
|
||||||
];
|
];
|
||||||
case 'GW':
|
case 'GW':
|
||||||
return [
|
return [
|
||||||
|
@ -19,6 +19,7 @@ class FactoryResetModel {
|
|||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
'devicesUuid': devicesUuid,
|
'devicesUuid': devicesUuid,
|
||||||
|
'operationType': operationType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ class FactoryResetModel {
|
|||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
return {
|
return {
|
||||||
'devicesUuid': devicesUuid,
|
'devicesUuid': devicesUuid,
|
||||||
|
'operationType': operationType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,3 +58,4 @@ class FactoryResetModel {
|
|||||||
@override
|
@override
|
||||||
int get hashCode => devicesUuid.hashCode;
|
int get hashCode => devicesUuid.hashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,8 @@ import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dar
|
|||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLayout {
|
class CeilingSensorControlsView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
const CeilingSensorControlsView({super.key, required this.device});
|
const CeilingSensorControlsView({super.key, required this.device});
|
||||||
|
|
||||||
final AllDevicesModel device;
|
final AllDevicesModel device;
|
||||||
@ -31,29 +32,35 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
|
|||||||
..add(CeilingInitialEvent(device.uuid ?? '')),
|
..add(CeilingInitialEvent(device.uuid ?? '')),
|
||||||
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
|
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state is CeilingLoadingInitialState || state is CeilingReportsLoadingState) {
|
if (state is CeilingLoadingInitialState ||
|
||||||
|
state is CeilingReportsLoadingState) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
} else if (state is CeilingUpdateState) {
|
} else if (state is CeilingUpdateState) {
|
||||||
return _buildGridView(
|
return _buildGridView(context, state.ceilingSensorModel,
|
||||||
context, state.ceilingSensorModel, isExtraLarge, isLarge, isMedium);
|
isExtraLarge, isLarge, isMedium);
|
||||||
} else if (state is CeilingReportsState) {
|
} else if (state is CeilingReportsState) {
|
||||||
return ReportsTable(
|
return ReportsTable(
|
||||||
report: state.deviceReport,
|
report: state.deviceReport,
|
||||||
onRowTap: (index) {},
|
onRowTap: (index) {},
|
||||||
onClose: () {
|
onClose: () {
|
||||||
context.read<CeilingSensorBloc>().add(BackToCeilingGridViewEvent());
|
context
|
||||||
|
.read<CeilingSensorBloc>()
|
||||||
|
.add(BackToCeilingGridViewEvent());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else if (state is ShowCeilingDescriptionState) {
|
} else if (state is ShowCeilingDescriptionState) {
|
||||||
return DescriptionView(
|
return DescriptionView(
|
||||||
description: state.description,
|
description: state.description,
|
||||||
onClose: () {
|
onClose: () {
|
||||||
context.read<CeilingSensorBloc>().add(BackToCeilingGridViewEvent());
|
context
|
||||||
|
.read<CeilingSensorBloc>()
|
||||||
|
.add(BackToCeilingGridViewEvent());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else if (state is CeilingReportsFailedState) {
|
} else if (state is CeilingReportsFailedState) {
|
||||||
final model = context.read<CeilingSensorBloc>().deviceStatus;
|
final model = context.read<CeilingSensorBloc>().deviceStatus;
|
||||||
return _buildGridView(context, model, isExtraLarge, isLarge, isMedium);
|
return _buildGridView(
|
||||||
|
context, model, isExtraLarge, isLarge, isMedium);
|
||||||
}
|
}
|
||||||
return const Center(child: Text('Error fetching status'));
|
return const Center(child: Text('Error fetching status'));
|
||||||
},
|
},
|
||||||
@ -61,8 +68,8 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildGridView(BuildContext context, CeilingSensorModel model, bool isExtraLarge,
|
Widget _buildGridView(BuildContext context, CeilingSensorModel model,
|
||||||
bool isLarge, bool isMedium) {
|
bool isExtraLarge, bool isLarge, bool isMedium) {
|
||||||
return GridView(
|
return GridView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
@ -143,8 +150,8 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
|
|||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.read<CeilingSensorBloc>().add(
|
context.read<CeilingSensorBloc>().add(GetCeilingDeviceReportsEvent(
|
||||||
GetCeilingDeviceReportsEvent(code: 'presence_state', deviceUuid: device.uuid!));
|
code: 'presence_state', deviceUuid: device.uuid!));
|
||||||
},
|
},
|
||||||
child: const PresenceStaticWidget(
|
child: const PresenceStaticWidget(
|
||||||
icon: Assets.illuminanceRecordIcon,
|
icon: Assets.illuminanceRecordIcon,
|
||||||
@ -153,9 +160,8 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
|
|||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context
|
context.read<CeilingSensorBloc>().add(GetCeilingDeviceReportsEvent(
|
||||||
.read<CeilingSensorBloc>()
|
code: '', deviceUuid: device.uuid!));
|
||||||
.add(GetCeilingDeviceReportsEvent(code: '', deviceUuid: device.uuid!));
|
|
||||||
},
|
},
|
||||||
child: const PresenceStaticWidget(
|
child: const PresenceStaticWidget(
|
||||||
icon: Assets.helpDescriptionIcon,
|
icon: Assets.helpDescriptionIcon,
|
||||||
|
@ -0,0 +1,250 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:firebase_database/firebase_database.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart';
|
||||||
|
import 'package:syncrow_web/services/batch_control_devices_service.dart';
|
||||||
|
import 'package:syncrow_web/services/control_device_service.dart';
|
||||||
|
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||||
|
|
||||||
|
part 'flush_mounted_presence_sensor_event.dart';
|
||||||
|
part 'flush_mounted_presence_sensor_state.dart';
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorBloc
|
||||||
|
extends Bloc<FlushMountedPresenceSensorEvent, FlushMountedPresenceSensorState> {
|
||||||
|
final String deviceId;
|
||||||
|
final ControlDeviceService controlDeviceService;
|
||||||
|
final BatchControlDevicesService batchControlDevicesService;
|
||||||
|
|
||||||
|
late FlushMountedPresenceSensorModel deviceStatus;
|
||||||
|
FlushMountedPresenceSensorBloc({
|
||||||
|
required this.deviceId,
|
||||||
|
required this.controlDeviceService,
|
||||||
|
required this.batchControlDevicesService,
|
||||||
|
}) : super(FlushMountedPresenceSensorInitialState()) {
|
||||||
|
on<FlushMountedPresenceSensorFetchStatusEvent>(
|
||||||
|
_onFlushMountedPresenceSensorFetchStatusEvent,
|
||||||
|
);
|
||||||
|
on<FlushMountedPresenceSensorFetchBatchStatusEvent>(
|
||||||
|
_onFlushMountedPresenceSensorFetchBatchStatusEvent);
|
||||||
|
on<FlushMountedPresenceSensorChangeValueEvent>(
|
||||||
|
_onFlushMountedPresenceSensorChangeValueEvent,
|
||||||
|
);
|
||||||
|
on<FlushMountedPresenceSensorBatchControlEvent>(
|
||||||
|
_onFlushMountedPresenceSensorBatchControlEvent,
|
||||||
|
);
|
||||||
|
on<FlushMountedPresenceSensorGetDeviceReportsEvent>(
|
||||||
|
_onFlushMountedPresenceSensorGetDeviceReportsEvent);
|
||||||
|
on<FlushMountedPresenceSensorShowDescriptionEvent>(
|
||||||
|
_onFlushMountedPresenceSensorShowDescriptionEvent,
|
||||||
|
);
|
||||||
|
on<FlushMountedPresenceSensorBackToGridViewEvent>(
|
||||||
|
_onFlushMountedPresenceSensorBackToGridViewEvent,
|
||||||
|
);
|
||||||
|
on<FlushMountedPresenceSensorFactoryResetEvent>(
|
||||||
|
_onFlushMountedPresenceSensorFactoryResetEvent,
|
||||||
|
);
|
||||||
|
on<FlushMountedPresenceSensorStatusUpdatedEvent>(
|
||||||
|
_onFlushMountedPresenceSensorStatusUpdatedEvent,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onFlushMountedPresenceSensorFetchStatusEvent(
|
||||||
|
FlushMountedPresenceSensorFetchStatusEvent event,
|
||||||
|
Emitter<FlushMountedPresenceSensorState> emit,
|
||||||
|
) async {
|
||||||
|
emit(FlushMountedPresenceSensorLoadingInitialState());
|
||||||
|
try {
|
||||||
|
final response = await DevicesManagementApi().getDeviceStatus(deviceId);
|
||||||
|
deviceStatus = FlushMountedPresenceSensorModel.fromJson(response.status);
|
||||||
|
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
|
||||||
|
_listenToChanges(deviceId);
|
||||||
|
} catch (e) {
|
||||||
|
emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onFlushMountedPresenceSensorFetchBatchStatusEvent(
|
||||||
|
FlushMountedPresenceSensorFetchBatchStatusEvent event,
|
||||||
|
Emitter<FlushMountedPresenceSensorState> emit,
|
||||||
|
) async {
|
||||||
|
emit(FlushMountedPresenceSensorLoadingInitialState());
|
||||||
|
try {
|
||||||
|
final response = await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
||||||
|
deviceStatus = FlushMountedPresenceSensorModel.fromJson(response.status);
|
||||||
|
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
|
||||||
|
} catch (e) {
|
||||||
|
emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _listenToChanges(String deviceId) {
|
||||||
|
try {
|
||||||
|
final ref = FirebaseDatabase.instance.ref(
|
||||||
|
'device-status/$deviceId',
|
||||||
|
);
|
||||||
|
|
||||||
|
ref.onValue.listen((event) {
|
||||||
|
final eventsMap = event.snapshot.value as Map<dynamic, dynamic>;
|
||||||
|
|
||||||
|
List<Status> statusList = [];
|
||||||
|
eventsMap['status'].forEach((element) {
|
||||||
|
statusList.add(
|
||||||
|
Status(code: element['code'], value: element['value']),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
deviceStatus = FlushMountedPresenceSensorModel.fromJson(statusList);
|
||||||
|
if (!isClosed) {
|
||||||
|
add(FlushMountedPresenceSensorStatusUpdatedEvent(deviceStatus));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (_) {
|
||||||
|
log(
|
||||||
|
'Error listening to changes',
|
||||||
|
name: 'FlushMountedPresenceSensorBloc._listenToChanges',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onFlushMountedPresenceSensorChangeValueEvent(
|
||||||
|
FlushMountedPresenceSensorChangeValueEvent event,
|
||||||
|
Emitter<FlushMountedPresenceSensorState> emit,
|
||||||
|
) async {
|
||||||
|
emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus));
|
||||||
|
_updateDeviceFunctionFromCode(event.code, event.value);
|
||||||
|
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
|
||||||
|
try {
|
||||||
|
await controlDeviceService.controlDevice(
|
||||||
|
deviceUuid: deviceId,
|
||||||
|
status: Status(code: event.code, value: event.value),
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
await _reloadDeviceStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onFlushMountedPresenceSensorBatchControlEvent(
|
||||||
|
FlushMountedPresenceSensorBatchControlEvent event,
|
||||||
|
Emitter<FlushMountedPresenceSensorState> emit,
|
||||||
|
) async {
|
||||||
|
emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus));
|
||||||
|
_updateDeviceFunctionFromCode(event.code, event.value);
|
||||||
|
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
|
||||||
|
|
||||||
|
try {
|
||||||
|
await batchControlDevicesService.batchControlDevices(
|
||||||
|
uuids: event.deviceIds,
|
||||||
|
code: event.code,
|
||||||
|
value: event.value,
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
await _reloadDeviceStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateDeviceFunctionFromCode(String code, int value) {
|
||||||
|
switch (code) {
|
||||||
|
case FlushMountedPresenceSensorModel.codeFarDetection:
|
||||||
|
deviceStatus.farDetection = value;
|
||||||
|
break;
|
||||||
|
case FlushMountedPresenceSensorModel.codeSensitivity:
|
||||||
|
deviceStatus.sensitivity = value;
|
||||||
|
break;
|
||||||
|
case FlushMountedPresenceSensorModel.codeNoneDelay:
|
||||||
|
deviceStatus.noneDelay = value;
|
||||||
|
break;
|
||||||
|
case FlushMountedPresenceSensorModel.codePresenceDelay:
|
||||||
|
deviceStatus.presenceDelay = value;
|
||||||
|
break;
|
||||||
|
case FlushMountedPresenceSensorModel.codeNearDetection:
|
||||||
|
deviceStatus.nearDetection = value;
|
||||||
|
break;
|
||||||
|
case FlushMountedPresenceSensorModel.codeOccurDistReduce:
|
||||||
|
deviceStatus.occurDistReduce = value;
|
||||||
|
break;
|
||||||
|
case FlushMountedPresenceSensorModel.codeSensiReduce:
|
||||||
|
deviceStatus.sensiReduce = value;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _reloadDeviceStatus() async {
|
||||||
|
await Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
|
add(FlushMountedPresenceSensorFetchStatusEvent());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onFlushMountedPresenceSensorGetDeviceReportsEvent(
|
||||||
|
FlushMountedPresenceSensorGetDeviceReportsEvent event,
|
||||||
|
Emitter<FlushMountedPresenceSensorState> emit,
|
||||||
|
) async {
|
||||||
|
emit(FlushMountedPresenceSensorDeviceReportsLoadingState());
|
||||||
|
|
||||||
|
try {
|
||||||
|
await DevicesManagementApi.getDeviceReports(deviceId, event.code)
|
||||||
|
.then((value) {
|
||||||
|
emit(FlushMountedPresenceSensorDeviceReportsState(
|
||||||
|
deviceReport: value, code: event.code));
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
emit(FlushMountedPresenceSensorDeviceReportsFailedState(error: e.toString()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onFlushMountedPresenceSensorShowDescriptionEvent(
|
||||||
|
FlushMountedPresenceSensorShowDescriptionEvent event,
|
||||||
|
Emitter<FlushMountedPresenceSensorState> emit,
|
||||||
|
) {
|
||||||
|
emit(FlushMountedPresenceSensorShowDescriptionState(
|
||||||
|
description: event.description));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onFlushMountedPresenceSensorBackToGridViewEvent(
|
||||||
|
FlushMountedPresenceSensorBackToGridViewEvent event,
|
||||||
|
Emitter<FlushMountedPresenceSensorState> emit,
|
||||||
|
) {
|
||||||
|
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onFlushMountedPresenceSensorFactoryResetEvent(
|
||||||
|
FlushMountedPresenceSensorFactoryResetEvent event,
|
||||||
|
Emitter<FlushMountedPresenceSensorState> emit,
|
||||||
|
) async {
|
||||||
|
emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus));
|
||||||
|
try {
|
||||||
|
final response = await DevicesManagementApi().factoryReset(
|
||||||
|
event.factoryReset,
|
||||||
|
event.deviceId,
|
||||||
|
);
|
||||||
|
if (!response) {
|
||||||
|
emit(
|
||||||
|
const FlushMountedPresenceSensorFailedState(
|
||||||
|
error: 'Something went wrong with factory reset, please try again',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onFlushMountedPresenceSensorStatusUpdatedEvent(
|
||||||
|
FlushMountedPresenceSensorStatusUpdatedEvent event,
|
||||||
|
Emitter<FlushMountedPresenceSensorState> emit,
|
||||||
|
) {
|
||||||
|
deviceStatus = event.model;
|
||||||
|
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
part of 'flush_mounted_presence_sensor_bloc.dart';
|
||||||
|
|
||||||
|
sealed class FlushMountedPresenceSensorEvent extends Equatable {
|
||||||
|
const FlushMountedPresenceSensorEvent();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorFetchStatusEvent
|
||||||
|
extends FlushMountedPresenceSensorEvent {}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorStatusUpdatedEvent
|
||||||
|
extends FlushMountedPresenceSensorEvent {
|
||||||
|
const FlushMountedPresenceSensorStatusUpdatedEvent(this.model);
|
||||||
|
|
||||||
|
final FlushMountedPresenceSensorModel model;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [model];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorChangeValueEvent
|
||||||
|
extends FlushMountedPresenceSensorEvent {
|
||||||
|
final int value;
|
||||||
|
final String code;
|
||||||
|
final bool isBatchControl;
|
||||||
|
const FlushMountedPresenceSensorChangeValueEvent({
|
||||||
|
required this.value,
|
||||||
|
required this.code,
|
||||||
|
this.isBatchControl = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [value, code];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorFetchBatchStatusEvent
|
||||||
|
extends FlushMountedPresenceSensorEvent {
|
||||||
|
final List<String> devicesIds;
|
||||||
|
const FlushMountedPresenceSensorFetchBatchStatusEvent(this.devicesIds);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesIds];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorGetDeviceReportsEvent
|
||||||
|
extends FlushMountedPresenceSensorEvent {
|
||||||
|
final String deviceUuid;
|
||||||
|
final String code;
|
||||||
|
const FlushMountedPresenceSensorGetDeviceReportsEvent({
|
||||||
|
required this.deviceUuid,
|
||||||
|
required this.code,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceUuid, code];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorShowDescriptionEvent
|
||||||
|
extends FlushMountedPresenceSensorEvent {
|
||||||
|
final String description;
|
||||||
|
const FlushMountedPresenceSensorShowDescriptionEvent({required this.description});
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorBackToGridViewEvent
|
||||||
|
extends FlushMountedPresenceSensorEvent {}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorBatchControlEvent
|
||||||
|
extends FlushMountedPresenceSensorEvent {
|
||||||
|
final List<String> deviceIds;
|
||||||
|
final String code;
|
||||||
|
final dynamic value;
|
||||||
|
|
||||||
|
const FlushMountedPresenceSensorBatchControlEvent({
|
||||||
|
required this.deviceIds,
|
||||||
|
required this.code,
|
||||||
|
required this.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceIds, code, value];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorFactoryResetEvent
|
||||||
|
extends FlushMountedPresenceSensorEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final FactoryResetModel factoryReset;
|
||||||
|
|
||||||
|
const FlushMountedPresenceSensorFactoryResetEvent({
|
||||||
|
required this.deviceId,
|
||||||
|
required this.factoryReset,
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
part of 'flush_mounted_presence_sensor_bloc.dart';
|
||||||
|
|
||||||
|
sealed class FlushMountedPresenceSensorState extends Equatable {
|
||||||
|
const FlushMountedPresenceSensorState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorInitialState
|
||||||
|
extends FlushMountedPresenceSensorState {}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorLoadingInitialState
|
||||||
|
extends FlushMountedPresenceSensorState {}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorUpdateState extends FlushMountedPresenceSensorState {
|
||||||
|
final FlushMountedPresenceSensorModel model;
|
||||||
|
const FlushMountedPresenceSensorUpdateState({required this.model});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [model];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorLoadingNewSate
|
||||||
|
extends FlushMountedPresenceSensorState {
|
||||||
|
final FlushMountedPresenceSensorModel model;
|
||||||
|
const FlushMountedPresenceSensorLoadingNewSate({required this.model});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [model];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorFailedState extends FlushMountedPresenceSensorState {
|
||||||
|
final String error;
|
||||||
|
|
||||||
|
const FlushMountedPresenceSensorFailedState({required this.error});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [error];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorDeviceReportsLoadingState
|
||||||
|
extends FlushMountedPresenceSensorState {}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorDeviceReportsState
|
||||||
|
extends FlushMountedPresenceSensorState {
|
||||||
|
const FlushMountedPresenceSensorDeviceReportsState({
|
||||||
|
required this.deviceReport,
|
||||||
|
required this.code,
|
||||||
|
});
|
||||||
|
|
||||||
|
final DeviceReport deviceReport;
|
||||||
|
final String code;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceReport, code];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorDeviceReportsFailedState
|
||||||
|
extends FlushMountedPresenceSensorState {
|
||||||
|
const FlushMountedPresenceSensorDeviceReportsFailedState({required this.error});
|
||||||
|
|
||||||
|
final String error;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [error];
|
||||||
|
}
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorShowDescriptionState
|
||||||
|
extends FlushMountedPresenceSensorState {
|
||||||
|
const FlushMountedPresenceSensorShowDescriptionState({required this.description});
|
||||||
|
|
||||||
|
final String description;
|
||||||
|
@override
|
||||||
|
List<Object> get props => [description];
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart';
|
||||||
|
import 'package:syncrow_web/services/batch_control_devices_service.dart';
|
||||||
|
import 'package:syncrow_web/services/control_device_service.dart';
|
||||||
|
|
||||||
|
abstract final class FlushMountedPresenceSensorBlocFactory {
|
||||||
|
const FlushMountedPresenceSensorBlocFactory._();
|
||||||
|
|
||||||
|
static FlushMountedPresenceSensorBloc create({
|
||||||
|
required String deviceId,
|
||||||
|
}) {
|
||||||
|
return FlushMountedPresenceSensorBloc(
|
||||||
|
deviceId: deviceId,
|
||||||
|
controlDeviceService: DebouncedControlDeviceService(
|
||||||
|
decoratee: RemoteControlDeviceService(),
|
||||||
|
),
|
||||||
|
batchControlDevicesService: DebouncedBatchControlDevicesService(
|
||||||
|
decoratee: RemoteBatchControlDevicesService(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorModel {
|
||||||
|
FlushMountedPresenceSensorModel({
|
||||||
|
required this.presenceState,
|
||||||
|
required this.farDetection,
|
||||||
|
required this.illuminance,
|
||||||
|
required this.sensitivity,
|
||||||
|
required this.occurDistReduce,
|
||||||
|
required this.noneDelay,
|
||||||
|
required this.presenceDelay,
|
||||||
|
required this.nearDetection,
|
||||||
|
required this.sensiReduce,
|
||||||
|
required this.checkingResult,
|
||||||
|
});
|
||||||
|
|
||||||
|
static const String codePresenceState = 'presence_state';
|
||||||
|
static const String codeSensitivity = 'sensitivity';
|
||||||
|
static const String codeNearDetection = 'near_detection';
|
||||||
|
static const String codeFarDetection = 'far_detection';
|
||||||
|
static const String codeCheckingResult = 'checking_result';
|
||||||
|
static const String codePresenceDelay = 'presence_delay';
|
||||||
|
static const String codeNoneDelay = 'none_delay';
|
||||||
|
static const String codeOccurDistReduce = 'occur_dist_reduce';
|
||||||
|
static const String codeIlluminance = 'illum_value';
|
||||||
|
static const String codeSensiReduce = 'sensi_reduce';
|
||||||
|
|
||||||
|
String presenceState;
|
||||||
|
int sensitivity;
|
||||||
|
int nearDetection;
|
||||||
|
int farDetection;
|
||||||
|
String checkingResult;
|
||||||
|
int presenceDelay;
|
||||||
|
int noneDelay;
|
||||||
|
int occurDistReduce;
|
||||||
|
int illuminance;
|
||||||
|
int sensiReduce;
|
||||||
|
|
||||||
|
factory FlushMountedPresenceSensorModel.fromJson(List<Status> jsonList) {
|
||||||
|
String presenceState = 'none';
|
||||||
|
int sensitivity = 0;
|
||||||
|
int nearDetection = 0;
|
||||||
|
int farDetection = 0;
|
||||||
|
String checkingResult = 'none';
|
||||||
|
int presenceDelay = 0;
|
||||||
|
int noneDelay = 0;
|
||||||
|
int occurDistReduce = 0;
|
||||||
|
int illuminance = 0;
|
||||||
|
int sensiReduce = 0;
|
||||||
|
|
||||||
|
for (var status in jsonList) {
|
||||||
|
switch (status.code) {
|
||||||
|
case codePresenceState:
|
||||||
|
presenceState = status.value ?? 'presence';
|
||||||
|
break;
|
||||||
|
case codeSensitivity:
|
||||||
|
sensitivity = status.value ?? 0;
|
||||||
|
break;
|
||||||
|
case codeNearDetection:
|
||||||
|
nearDetection = status.value ?? 0;
|
||||||
|
break;
|
||||||
|
case codeFarDetection:
|
||||||
|
farDetection = status.value ?? 0;
|
||||||
|
break;
|
||||||
|
case codeCheckingResult:
|
||||||
|
checkingResult = status.value ?? 'check_success';
|
||||||
|
break;
|
||||||
|
case codePresenceDelay:
|
||||||
|
presenceDelay = status.value ?? 0;
|
||||||
|
break;
|
||||||
|
case codeNoneDelay:
|
||||||
|
noneDelay = status.value ?? 0;
|
||||||
|
break;
|
||||||
|
case codeOccurDistReduce:
|
||||||
|
occurDistReduce = status.value ?? 0;
|
||||||
|
break;
|
||||||
|
case codeIlluminance:
|
||||||
|
illuminance = status.value ?? 0;
|
||||||
|
break;
|
||||||
|
case codeSensiReduce:
|
||||||
|
sensiReduce = status.value ?? 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FlushMountedPresenceSensorModel(
|
||||||
|
presenceState: presenceState,
|
||||||
|
sensitivity: sensitivity,
|
||||||
|
nearDetection: nearDetection,
|
||||||
|
farDetection: farDetection,
|
||||||
|
checkingResult: checkingResult,
|
||||||
|
presenceDelay: presenceDelay,
|
||||||
|
noneDelay: noneDelay,
|
||||||
|
occurDistReduce: occurDistReduce,
|
||||||
|
illuminance: illuminance,
|
||||||
|
sensiReduce: sensiReduce,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,183 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/factories/flush_mounted_presence_sensor_bloc_factory.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart';
|
||||||
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
|
const FlushMountedPresenceSensorBatchControlView({
|
||||||
|
required this.devicesIds,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<String> devicesIds;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => FlushMountedPresenceSensorBlocFactory.create(
|
||||||
|
deviceId: devicesIds.first,
|
||||||
|
)..add(FlushMountedPresenceSensorFetchBatchStatusEvent(devicesIds)),
|
||||||
|
child: BlocBuilder<FlushMountedPresenceSensorBloc,
|
||||||
|
FlushMountedPresenceSensorState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is FlushMountedPresenceSensorLoadingInitialState ||
|
||||||
|
state is FlushMountedPresenceSensorDeviceReportsLoadingState) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
} else if (state is FlushMountedPresenceSensorUpdateState) {
|
||||||
|
return _buildGridView(context, state.model);
|
||||||
|
}
|
||||||
|
return const Center(child: Text('Error fetching status'));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildGridView(
|
||||||
|
BuildContext context,
|
||||||
|
FlushMountedPresenceSensorModel model,
|
||||||
|
) {
|
||||||
|
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||||
|
final isLarge = isLargeScreenSize(context);
|
||||||
|
final isMedium = isMediumScreenSize(context);
|
||||||
|
return GridView(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: isLarge || isExtraLarge
|
||||||
|
? 3
|
||||||
|
: isMedium
|
||||||
|
? 2
|
||||||
|
: 1,
|
||||||
|
mainAxisExtent: 140,
|
||||||
|
crossAxisSpacing: 12,
|
||||||
|
mainAxisSpacing: 12,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
PresenceUpdateData(
|
||||||
|
value: model.sensitivity.toDouble(),
|
||||||
|
title: 'Sensitivity:',
|
||||||
|
minValue: 0,
|
||||||
|
maxValue: 9,
|
||||||
|
steps: 1,
|
||||||
|
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
|
FlushMountedPresenceSensorBatchControlEvent(
|
||||||
|
deviceIds: devicesIds,
|
||||||
|
code: FlushMountedPresenceSensorModel.codeSensitivity,
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PresenceUpdateData(
|
||||||
|
value: (model.nearDetection / 100).toDouble(),
|
||||||
|
title: 'Nearest Detect Dist:',
|
||||||
|
description: 'm',
|
||||||
|
minValue: 0.0,
|
||||||
|
maxValue: 9.5,
|
||||||
|
steps: 0.1,
|
||||||
|
valuesPercision: 1,
|
||||||
|
action: (double value) =>
|
||||||
|
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
|
FlushMountedPresenceSensorBatchControlEvent(
|
||||||
|
deviceIds: devicesIds,
|
||||||
|
code: FlushMountedPresenceSensorModel.codeNearDetection,
|
||||||
|
value: (value * 100).toInt(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PresenceUpdateData(
|
||||||
|
value: (model.farDetection / 100).toDouble(),
|
||||||
|
title: 'Max Detect Dist:',
|
||||||
|
description: 'm',
|
||||||
|
minValue: 0.0,
|
||||||
|
maxValue: 9.5,
|
||||||
|
steps: 0.1,
|
||||||
|
valuesPercision: 1,
|
||||||
|
action: (double value) =>
|
||||||
|
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
|
FlushMountedPresenceSensorBatchControlEvent(
|
||||||
|
deviceIds: devicesIds,
|
||||||
|
code: FlushMountedPresenceSensorModel.codeFarDetection,
|
||||||
|
value: (value * 100).toInt(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PresenceUpdateData(
|
||||||
|
value: model.presenceDelay.toDouble(),
|
||||||
|
title: 'Trigger Level:',
|
||||||
|
minValue: 0,
|
||||||
|
maxValue: 3,
|
||||||
|
steps: 1,
|
||||||
|
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
|
FlushMountedPresenceSensorBatchControlEvent(
|
||||||
|
deviceIds: devicesIds,
|
||||||
|
code: FlushMountedPresenceSensorModel.codePresenceDelay,
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PresenceUpdateData(
|
||||||
|
value: model.occurDistReduce.toDouble(),
|
||||||
|
title: 'Indent Level:',
|
||||||
|
minValue: 0,
|
||||||
|
maxValue: 3,
|
||||||
|
steps: 1,
|
||||||
|
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
|
FlushMountedPresenceSensorBatchControlEvent(
|
||||||
|
deviceIds: devicesIds,
|
||||||
|
code: FlushMountedPresenceSensorModel.codeOccurDistReduce,
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PresenceUpdateData(
|
||||||
|
value: (model.sensiReduce.toDouble()),
|
||||||
|
title: 'Target Confirm Time:',
|
||||||
|
description: 's',
|
||||||
|
minValue: 0,
|
||||||
|
maxValue: 3,
|
||||||
|
steps: 1,
|
||||||
|
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
|
FlushMountedPresenceSensorBatchControlEvent(
|
||||||
|
deviceIds: devicesIds,
|
||||||
|
code: FlushMountedPresenceSensorModel.codeSensiReduce,
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PresenceUpdateData(
|
||||||
|
value: ((model.noneDelay / 10).toDouble()),
|
||||||
|
description: 's',
|
||||||
|
title: 'Disappe Delay:',
|
||||||
|
minValue: 20,
|
||||||
|
maxValue: 300,
|
||||||
|
steps: 1,
|
||||||
|
action: (double value) =>
|
||||||
|
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
|
FlushMountedPresenceSensorBatchControlEvent(
|
||||||
|
deviceIds: devicesIds,
|
||||||
|
code: FlushMountedPresenceSensorModel.codeNoneDelay,
|
||||||
|
value: (value * 10).round(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
FactoryResetWidget(
|
||||||
|
callFactoryReset: () {
|
||||||
|
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
|
FlushMountedPresenceSensorFactoryResetEvent(
|
||||||
|
deviceId: devicesIds.first,
|
||||||
|
factoryReset: FactoryResetModel(devicesUuid: devicesIds),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,217 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/factories/flush_mounted_presence_sensor_bloc_factory.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_display_data.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_static_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_status.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/table/description_view.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
|
class FlushMountedPresenceSensorControlView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
|
const FlushMountedPresenceSensorControlView({super.key, required this.device});
|
||||||
|
|
||||||
|
final AllDevicesModel device;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => FlushMountedPresenceSensorBlocFactory.create(
|
||||||
|
deviceId: device.uuid ?? '-1',
|
||||||
|
)..add(FlushMountedPresenceSensorFetchStatusEvent()),
|
||||||
|
child: BlocBuilder<FlushMountedPresenceSensorBloc,
|
||||||
|
FlushMountedPresenceSensorState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is FlushMountedPresenceSensorLoadingInitialState ||
|
||||||
|
state is FlushMountedPresenceSensorDeviceReportsLoadingState) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
} else if (state is FlushMountedPresenceSensorUpdateState) {
|
||||||
|
return _buildGridView(context, state.model);
|
||||||
|
} else if (state is FlushMountedPresenceSensorDeviceReportsState) {
|
||||||
|
return ReportsTable(
|
||||||
|
report: state.deviceReport,
|
||||||
|
thirdColumnTitle:
|
||||||
|
state.code == 'illuminance_value' ? "Value" : 'Status',
|
||||||
|
thirdColumnDescription:
|
||||||
|
state.code == 'illuminance_value' ? "Lux" : null,
|
||||||
|
onRowTap: (index) {},
|
||||||
|
onClose: () {
|
||||||
|
context
|
||||||
|
.read<FlushMountedPresenceSensorBloc>()
|
||||||
|
.add(FlushMountedPresenceSensorBackToGridViewEvent());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (state is FlushMountedPresenceSensorShowDescriptionState) {
|
||||||
|
return DescriptionView(
|
||||||
|
description: state.description,
|
||||||
|
onClose: () {
|
||||||
|
context
|
||||||
|
.read<FlushMountedPresenceSensorBloc>()
|
||||||
|
.add(FlushMountedPresenceSensorBackToGridViewEvent());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else if (state is FlushMountedPresenceSensorDeviceReportsFailedState) {
|
||||||
|
final model =
|
||||||
|
context.read<FlushMountedPresenceSensorBloc>().deviceStatus;
|
||||||
|
return _buildGridView(context, model);
|
||||||
|
}
|
||||||
|
return const Center(
|
||||||
|
child: Text('Error fetching status', textAlign: TextAlign.center),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildGridView(
|
||||||
|
BuildContext context,
|
||||||
|
FlushMountedPresenceSensorModel model,
|
||||||
|
) {
|
||||||
|
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||||
|
final isLarge = isLargeScreenSize(context);
|
||||||
|
final isMedium = isMediumScreenSize(context);
|
||||||
|
return GridView(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: isLarge || isExtraLarge
|
||||||
|
? 3
|
||||||
|
: isMedium
|
||||||
|
? 2
|
||||||
|
: 1,
|
||||||
|
mainAxisExtent: 140,
|
||||||
|
crossAxisSpacing: 12,
|
||||||
|
mainAxisSpacing: 12,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
PresenceState(
|
||||||
|
value: model.presenceState,
|
||||||
|
),
|
||||||
|
PresenceDisplayValue(
|
||||||
|
value: model.illuminance.toString(),
|
||||||
|
postfix: 'Lux',
|
||||||
|
description: 'Illuminance Value',
|
||||||
|
),
|
||||||
|
PresenceUpdateData(
|
||||||
|
value: model.sensitivity.toDouble(),
|
||||||
|
title: 'Sensitivity:',
|
||||||
|
minValue: 0,
|
||||||
|
maxValue: 9,
|
||||||
|
steps: 1,
|
||||||
|
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
|
FlushMountedPresenceSensorChangeValueEvent(
|
||||||
|
code: FlushMountedPresenceSensorModel.codeSensitivity,
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PresenceUpdateData(
|
||||||
|
value: (model.nearDetection / 100).toDouble(),
|
||||||
|
title: 'Nearest Detect Dist:',
|
||||||
|
description: 'm',
|
||||||
|
minValue: 0.0,
|
||||||
|
maxValue: 9.5,
|
||||||
|
steps: 0.1,
|
||||||
|
valuesPercision: 1,
|
||||||
|
action: (double value) =>
|
||||||
|
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
|
FlushMountedPresenceSensorChangeValueEvent(
|
||||||
|
code: FlushMountedPresenceSensorModel.codeNearDetection,
|
||||||
|
value: (value * 100).toInt(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PresenceUpdateData(
|
||||||
|
value: (model.farDetection / 100).toDouble(),
|
||||||
|
title: 'Max Detect Dist:',
|
||||||
|
description: 'm',
|
||||||
|
minValue: 0.0,
|
||||||
|
maxValue: 9.5,
|
||||||
|
steps: 0.1,
|
||||||
|
valuesPercision: 1,
|
||||||
|
action: (double value) =>
|
||||||
|
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
|
FlushMountedPresenceSensorChangeValueEvent(
|
||||||
|
code: FlushMountedPresenceSensorModel.codeFarDetection,
|
||||||
|
value: (value * 100).toInt(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PresenceUpdateData(
|
||||||
|
value: (model.presenceDelay.toDouble()),
|
||||||
|
title: 'Trigger Level:',
|
||||||
|
minValue: 0,
|
||||||
|
maxValue: 3,
|
||||||
|
steps: 1,
|
||||||
|
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
|
FlushMountedPresenceSensorChangeValueEvent(
|
||||||
|
code: FlushMountedPresenceSensorModel.codePresenceDelay,
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PresenceUpdateData(
|
||||||
|
value: (model.occurDistReduce.toDouble()),
|
||||||
|
title: 'Indent Level:',
|
||||||
|
minValue: 0,
|
||||||
|
maxValue: 3,
|
||||||
|
steps: 1,
|
||||||
|
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
|
FlushMountedPresenceSensorChangeValueEvent(
|
||||||
|
code: FlushMountedPresenceSensorModel.codeOccurDistReduce,
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PresenceUpdateData(
|
||||||
|
value: (model.sensiReduce.toDouble()),
|
||||||
|
title: 'Target Confirm Time:',
|
||||||
|
description: 's',
|
||||||
|
minValue: 0,
|
||||||
|
maxValue: 3,
|
||||||
|
steps: 1,
|
||||||
|
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
|
FlushMountedPresenceSensorChangeValueEvent(
|
||||||
|
code: FlushMountedPresenceSensorModel.codeSensiReduce,
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PresenceUpdateData(
|
||||||
|
value: ((model.noneDelay / 10).toDouble()),
|
||||||
|
description: 's',
|
||||||
|
title: 'Disappe Delay:',
|
||||||
|
minValue: 20,
|
||||||
|
maxValue: 300,
|
||||||
|
steps: 1,
|
||||||
|
action: (double value) =>
|
||||||
|
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
|
FlushMountedPresenceSensorChangeValueEvent(
|
||||||
|
code: FlushMountedPresenceSensorModel.codeNoneDelay,
|
||||||
|
value: (value * 10).round(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||||
|
FlushMountedPresenceSensorGetDeviceReportsEvent(
|
||||||
|
code: 'presence_state',
|
||||||
|
deviceUuid: device.uuid!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const PresenceStaticWidget(
|
||||||
|
icon: Assets.presenceRecordIcon,
|
||||||
|
description: 'Presence Record',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,6 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_re
|
|||||||
import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_bloc.dart';
|
import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_event.dart';
|
import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_event.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
|
||||||
// import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
|
|
||||||
|
|
||||||
class MainDoorSensorBatchView extends StatelessWidget {
|
class MainDoorSensorBatchView extends StatelessWidget {
|
||||||
const MainDoorSensorBatchView({super.key, required this.devicesIds});
|
const MainDoorSensorBatchView({super.key, required this.devicesIds});
|
||||||
@ -13,26 +12,19 @@ class MainDoorSensorBatchView extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => MainDoorSensorBloc(),
|
||||||
|
child: Builder(
|
||||||
|
builder: (innerContext) {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
// SizedBox(
|
|
||||||
// width: 170,
|
|
||||||
// height: 140,
|
|
||||||
// child: FirmwareUpdateWidget(
|
|
||||||
// deviceId: devicesIds.first,
|
|
||||||
// version: 12,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// const SizedBox(
|
|
||||||
// width: 12,
|
|
||||||
// ),
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 170,
|
width: 170,
|
||||||
height: 140,
|
height: 140,
|
||||||
child: FactoryResetWidget(
|
child: FactoryResetWidget(
|
||||||
callFactoryReset: () {
|
callFactoryReset: () {
|
||||||
BlocProvider.of<MainDoorSensorBloc>(context).add(
|
BlocProvider.of<MainDoorSensorBloc>(innerContext).add(
|
||||||
MainDoorSensorFactoryReset(
|
MainDoorSensorFactoryReset(
|
||||||
deviceId: devicesIds.first,
|
deviceId: devicesIds.first,
|
||||||
factoryReset: FactoryResetModel(devicesUuid: devicesIds),
|
factoryReset: FactoryResetModel(devicesUuid: devicesIds),
|
||||||
@ -43,5 +35,8 @@ class MainDoorSensorBatchView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/shared/increament_decreament.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/increament_decreament.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
class PresenceUpdateData extends StatefulWidget {
|
class PresenceUpdateData extends StatefulWidget {
|
||||||
const PresenceUpdateData({
|
const PresenceUpdateData({
|
||||||
@ -13,6 +14,7 @@ class PresenceUpdateData extends StatefulWidget {
|
|||||||
required this.maxValue,
|
required this.maxValue,
|
||||||
required this.steps,
|
required this.steps,
|
||||||
this.description,
|
this.description,
|
||||||
|
this.valuesPercision = 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String title;
|
final String title;
|
||||||
@ -22,6 +24,7 @@ class PresenceUpdateData extends StatefulWidget {
|
|||||||
final double steps;
|
final double steps;
|
||||||
final Function action;
|
final Function action;
|
||||||
final String? description;
|
final String? description;
|
||||||
|
final int valuesPercision;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PresenceUpdateData> createState() => _CurrentTempState();
|
State<PresenceUpdateData> createState() => _CurrentTempState();
|
||||||
@ -45,7 +48,7 @@ class _CurrentTempState extends State<PresenceUpdateData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onValueChanged(double newValue) {
|
void _onValueChanged(double newValue) {
|
||||||
widget.action(newValue.toInt());
|
widget.action(newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -62,11 +65,14 @@ class _CurrentTempState extends State<PresenceUpdateData> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
widget.title,
|
widget.title,
|
||||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
color: ColorsManager.blackColor, fontWeight: FontWeight.w400, fontSize: 10),
|
color: ColorsManager.blackColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 10,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
IncrementDecrementWidget(
|
IncrementDecrementWidget(
|
||||||
value: widget.value.toString(),
|
value: widget.value.toStringAsFixed(widget.valuesPercision),
|
||||||
description: widget.description ?? '',
|
description: widget.description ?? '',
|
||||||
descriptionColor: ColorsManager.blackColor,
|
descriptionColor: ColorsManager.blackColor,
|
||||||
onIncrement: () {
|
onIncrement: () {
|
||||||
|
@ -84,6 +84,16 @@ class _PresenceUpdateDataState extends State<PresenceNoBodyTime> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(PresenceNoBodyTime oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (oldWidget.value != widget.value) {
|
||||||
|
setState(() {
|
||||||
|
_currentValue = widget.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DeviceControlsContainer(
|
return DeviceControlsContainer(
|
||||||
|
@ -62,9 +62,6 @@ class ToggleWidget extends StatelessWidget {
|
|||||||
)),
|
)),
|
||||||
if (showToggle)
|
if (showToggle)
|
||||||
Container(
|
Container(
|
||||||
height: 20,
|
|
||||||
width: 35,
|
|
||||||
padding: const EdgeInsets.only(right: 16, top: 10),
|
|
||||||
child: CupertinoSwitch(
|
child: CupertinoSwitch(
|
||||||
value: value,
|
value: value,
|
||||||
activeColor: ColorsManager.dialogBlueTitle,
|
activeColor: ColorsManager.dialogBlueTitle,
|
||||||
|
@ -13,7 +13,8 @@ import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_la
|
|||||||
|
|
||||||
import '../models/sos_status_model.dart';
|
import '../models/sos_status_model.dart';
|
||||||
|
|
||||||
class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
|
class SosDeviceControlsView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
const SosDeviceControlsView({
|
const SosDeviceControlsView({
|
||||||
super.key,
|
super.key,
|
||||||
required this.device,
|
required this.device,
|
||||||
@ -24,7 +25,8 @@ class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => SosDeviceBloc()..add(GetDeviceStatus(device.uuid!)),
|
create: (context) =>
|
||||||
|
SosDeviceBloc()..add(GetDeviceStatus(device.uuid!)),
|
||||||
child: BlocBuilder<SosDeviceBloc, SosDeviceState>(
|
child: BlocBuilder<SosDeviceBloc, SosDeviceState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state is SosDeviceLoadingState) {
|
if (state is SosDeviceLoadingState) {
|
||||||
@ -63,7 +65,8 @@ class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildStatusControls(BuildContext context, SosStatusModel deviceStatus) {
|
Widget _buildStatusControls(
|
||||||
|
BuildContext context, SosStatusModel deviceStatus) {
|
||||||
final isExtraLarge = isExtraLargeScreenSize(context);
|
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||||
final isLarge = isLargeScreenSize(context);
|
final isLarge = isLargeScreenSize(context);
|
||||||
final isMedium = isMediumScreenSize(context);
|
final isMedium = isMediumScreenSize(context);
|
||||||
@ -85,7 +88,7 @@ class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout
|
|||||||
IconNameStatusContainer(
|
IconNameStatusContainer(
|
||||||
isFullIcon: false,
|
isFullIcon: false,
|
||||||
name: deviceStatus.sosStatus == 'sos' ? 'SOS' : 'Normal',
|
name: deviceStatus.sosStatus == 'sos' ? 'SOS' : 'Normal',
|
||||||
icon: deviceStatus.sosStatus == 'sos' ? Assets.sos : Assets.sosNormal,
|
icon: deviceStatus.sosStatus == 'sos' ? Assets.sosNormal : Assets.sos,
|
||||||
onTap: () {},
|
onTap: () {},
|
||||||
status: false,
|
status: false,
|
||||||
textColor: ColorsManager.blackColor,
|
textColor: ColorsManager.blackColor,
|
||||||
|
@ -21,6 +21,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
|||||||
on<ShowDescriptionEvent>(_showDescription);
|
on<ShowDescriptionEvent>(_showDescription);
|
||||||
on<BackToGridViewEvent>(_backToGridView);
|
on<BackToGridViewEvent>(_backToGridView);
|
||||||
on<WallSensorFactoryResetEvent>(_onFactoryReset);
|
on<WallSensorFactoryResetEvent>(_onFactoryReset);
|
||||||
|
on<WallSensorRealtimeUpdateEvent>(_onRealtimeUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _fetchWallSensorStatus(
|
void _fetchWallSensorStatus(
|
||||||
@ -30,7 +31,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
|||||||
var response = await DevicesManagementApi().getDeviceStatus(deviceId);
|
var response = await DevicesManagementApi().getDeviceStatus(deviceId);
|
||||||
deviceStatus = WallSensorModel.fromJson(response.status);
|
deviceStatus = WallSensorModel.fromJson(response.status);
|
||||||
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
||||||
_listenToChanges(emit, deviceId);
|
_listenToChanges(deviceId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(WallSensorFailedState(error: e.toString()));
|
emit(WallSensorFailedState(error: e.toString()));
|
||||||
return;
|
return;
|
||||||
@ -52,27 +53,26 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_listenToChanges(Emitter<WallSensorState> emit, deviceId) {
|
void _listenToChanges(String deviceId) {
|
||||||
try {
|
|
||||||
DatabaseReference ref =
|
DatabaseReference ref =
|
||||||
FirebaseDatabase.instance.ref('device-status/$deviceId');
|
FirebaseDatabase.instance.ref('device-status/$deviceId');
|
||||||
Stream<DatabaseEvent> stream = ref.onValue;
|
ref.onValue.listen((DatabaseEvent event) {
|
||||||
|
final data = event.snapshot.value as Map<dynamic, dynamic>?;
|
||||||
|
if (data == null) return;
|
||||||
|
|
||||||
stream.listen((DatabaseEvent event) {
|
final statusList = (data['status'] as List?)
|
||||||
Map<dynamic, dynamic> usersMap =
|
?.map((e) => Status(code: e['code'], value: e['value']))
|
||||||
event.snapshot.value as Map<dynamic, dynamic>;
|
.toList();
|
||||||
List<Status> statusList = [];
|
|
||||||
|
|
||||||
usersMap['status'].forEach((element) {
|
if (statusList != null) {
|
||||||
statusList
|
final updatedDeviceStatus = WallSensorModel.fromJson(statusList);
|
||||||
.add(Status(code: element['code'], value: element['value']));
|
if (!isClosed) {
|
||||||
});
|
add(WallSensorRealtimeUpdateEvent(updatedDeviceStatus));
|
||||||
|
|
||||||
deviceStatus = WallSensorModel.fromJson(statusList);
|
|
||||||
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
|
|
||||||
});
|
|
||||||
} catch (_) {}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void _changeValue(
|
void _changeValue(
|
||||||
WallSensorChangeValueEvent event, Emitter<WallSensorState> emit) async {
|
WallSensorChangeValueEvent event, Emitter<WallSensorState> emit) async {
|
||||||
@ -195,4 +195,12 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
|||||||
emit(WallSensorFailedState(error: e.toString()));
|
emit(WallSensorFailedState(error: e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onRealtimeUpdate(
|
||||||
|
WallSensorRealtimeUpdateEvent event,
|
||||||
|
Emitter<WallSensorState> emit,
|
||||||
|
) {
|
||||||
|
deviceStatus = event.deviceStatus;
|
||||||
|
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/wall_sensor/model/wall_sensor_model.dart';
|
||||||
|
|
||||||
abstract class WallSensorEvent extends Equatable {
|
abstract class WallSensorEvent extends Equatable {
|
||||||
const WallSensorEvent();
|
const WallSensorEvent();
|
||||||
@ -70,3 +71,8 @@ class WallSensorFactoryResetEvent extends WallSensorEvent {
|
|||||||
required this.factoryReset,
|
required this.factoryReset,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class WallSensorRealtimeUpdateEvent extends WallSensorEvent {
|
||||||
|
final WallSensorModel deviceStatus;
|
||||||
|
const WallSensorRealtimeUpdateEvent(this.deviceStatus);
|
||||||
|
}
|
||||||
|
@ -30,6 +30,7 @@ class FunctionBloc extends Bloc<FunctionBlocEvent, FunctionBlocState> {
|
|||||||
condition: event.functionData.condition ?? existingData.condition,
|
condition: event.functionData.condition ?? existingData.condition,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
functions.clear();
|
||||||
functions.add(event.functionData);
|
functions.add(event.functionData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,8 +64,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
TriggerSwitchTabsEvent event,
|
TriggerSwitchTabsEvent event,
|
||||||
Emitter<RoutineState> emit,
|
Emitter<RoutineState> emit,
|
||||||
) {
|
) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(routineTab: event.isRoutineTab, createRoutineView: false));
|
||||||
routineTab: event.isRoutineTab, createRoutineView: false));
|
|
||||||
add(ResetRoutineState());
|
add(ResetRoutineState());
|
||||||
if (event.isRoutineTab) {
|
if (event.isRoutineTab) {
|
||||||
add(const LoadScenes());
|
add(const LoadScenes());
|
||||||
@ -91,8 +90,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems);
|
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems);
|
||||||
|
|
||||||
// Find the index of the item in teh current itemsList
|
// Find the index of the item in teh current itemsList
|
||||||
int index = updatedIfItems.indexWhere(
|
int index =
|
||||||
(map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
updatedIfItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
||||||
// Replace the map if the index is valid
|
// Replace the map if the index is valid
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
updatedIfItems[index] = event.item;
|
updatedIfItems[index] = event.item;
|
||||||
@ -101,21 +100,18 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (event.isTabToRun) {
|
if (event.isTabToRun) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
|
||||||
ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
|
|
||||||
} else {
|
} else {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
|
||||||
ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAddToThenContainer(
|
void _onAddToThenContainer(AddToThenContainer event, Emitter<RoutineState> emit) {
|
||||||
AddToThenContainer event, Emitter<RoutineState> emit) {
|
|
||||||
final currentItems = List<Map<String, dynamic>>.from(state.thenItems);
|
final currentItems = List<Map<String, dynamic>>.from(state.thenItems);
|
||||||
|
|
||||||
// Find the index of the item in teh current itemsList
|
// Find the index of the item in teh current itemsList
|
||||||
int index = currentItems.indexWhere(
|
int index =
|
||||||
(map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
currentItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
||||||
// Replace the map if the index is valid
|
// Replace the map if the index is valid
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
currentItems[index] = event.item;
|
currentItems[index] = event.item;
|
||||||
@ -126,45 +122,42 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
emit(state.copyWith(thenItems: currentItems));
|
emit(state.copyWith(thenItems: currentItems));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onAddFunctionsToRoutine(
|
void _onAddFunctionsToRoutine(AddFunctionToRoutine event, Emitter<RoutineState> emit) {
|
||||||
AddFunctionToRoutine event, Emitter<RoutineState> emit) {
|
|
||||||
try {
|
try {
|
||||||
if (event.functions.isEmpty) return;
|
if (event.functions.isEmpty) return;
|
||||||
|
|
||||||
List<DeviceFunctionData> selectedFunction =
|
// List<DeviceFunctionData> selectedFunction = List<DeviceFunctionData>.from(event.functions);
|
||||||
List<DeviceFunctionData>.from(event.functions);
|
|
||||||
|
|
||||||
Map<String, List<DeviceFunctionData>> currentSelectedFunctions =
|
Map<String, List<DeviceFunctionData>> currentSelectedFunctions =
|
||||||
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
||||||
if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) {
|
|
||||||
List<DeviceFunctionData> currentFunctions =
|
|
||||||
List<DeviceFunctionData>.from(
|
|
||||||
currentSelectedFunctions[event.uniqueCustomId] ?? []);
|
|
||||||
|
|
||||||
List<String> functionCode = [];
|
// if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) {
|
||||||
for (int i = 0; i < selectedFunction.length; i++) {
|
// List<DeviceFunctionData> currentFunctions =
|
||||||
for (int j = 0; j < currentFunctions.length; j++) {
|
// List<DeviceFunctionData>.from(currentSelectedFunctions[event.uniqueCustomId] ?? []);
|
||||||
if (selectedFunction[i].functionCode ==
|
|
||||||
currentFunctions[j].functionCode) {
|
|
||||||
currentFunctions[j] = selectedFunction[i];
|
|
||||||
if (!functionCode.contains(currentFunctions[j].functionCode)) {
|
|
||||||
functionCode.add(currentFunctions[j].functionCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < functionCode.length; i++) {
|
// List<String> functionCode = [];
|
||||||
selectedFunction
|
// for (int i = 0; i < selectedFunction.length; i++) {
|
||||||
.removeWhere((code) => code.functionCode == functionCode[i]);
|
// for (int j = 0; j < currentFunctions.length; j++) {
|
||||||
}
|
// if (selectedFunction[i].functionCode == currentFunctions[j].functionCode) {
|
||||||
|
// currentFunctions[j] = selectedFunction[i];
|
||||||
|
// if (!functionCode.contains(currentFunctions[j].functionCode)) {
|
||||||
|
// functionCode.add(currentFunctions[j].functionCode);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
currentSelectedFunctions[event.uniqueCustomId] =
|
// for (int i = 0; i < functionCode.length; i++) {
|
||||||
List.from(currentFunctions)..addAll(selectedFunction);
|
// selectedFunction.removeWhere((code) => code.functionCode == functionCode[i]);
|
||||||
} else {
|
// }
|
||||||
currentSelectedFunctions[event.uniqueCustomId] =
|
|
||||||
List.from(event.functions);
|
// currentSelectedFunctions[event.uniqueCustomId] = List.from(currentFunctions)
|
||||||
}
|
// ..addAll(selectedFunction);
|
||||||
|
// } else {
|
||||||
|
// currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions);
|
||||||
|
// }
|
||||||
|
|
||||||
|
currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions);
|
||||||
|
|
||||||
emit(state.copyWith(selectedFunctions: currentSelectedFunctions));
|
emit(state.copyWith(selectedFunctions: currentSelectedFunctions));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -172,30 +165,24 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onLoadScenes(
|
Future<void> _onLoadScenes(LoadScenes event, Emitter<RoutineState> emit) async {
|
||||||
LoadScenes event, Emitter<RoutineState> emit) async {
|
|
||||||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||||
List<ScenesModel> scenes = [];
|
List<ScenesModel> scenes = [];
|
||||||
try {
|
try {
|
||||||
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
||||||
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
if (createRoutineBloc.selectedSpaceId == '' &&
|
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
|
||||||
createRoutineBloc.selectedCommunityId == '') {
|
|
||||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||||
for (var communityId in spaceBloc.state.selectedCommunities) {
|
for (var communityId in spaceBloc.state.selectedCommunities) {
|
||||||
List<String> spacesList =
|
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||||
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
|
||||||
for (var spaceId in spacesList) {
|
for (var spaceId in spacesList) {
|
||||||
scenes.addAll(
|
scenes.addAll(await SceneApi.getScenes(spaceId, communityId, projectUuid));
|
||||||
await SceneApi.getScenes(spaceId, communityId, projectUuid));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
scenes.addAll(await SceneApi.getScenes(
|
scenes.addAll(await SceneApi.getScenes(
|
||||||
createRoutineBloc.selectedSpaceId,
|
createRoutineBloc.selectedSpaceId, createRoutineBloc.selectedCommunityId, projectUuid));
|
||||||
createRoutineBloc.selectedCommunityId,
|
|
||||||
projectUuid));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
@ -212,8 +199,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onLoadAutomation(
|
Future<void> _onLoadAutomation(LoadAutomation event, Emitter<RoutineState> emit) async {
|
||||||
LoadAutomation event, Emitter<RoutineState> emit) async {
|
|
||||||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||||
List<ScenesModel> automations = [];
|
List<ScenesModel> automations = [];
|
||||||
final projectId = await ProjectManager.getProjectUUID() ?? '';
|
final projectId = await ProjectManager.getProjectUUID() ?? '';
|
||||||
@ -221,23 +207,17 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
||||||
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
||||||
try {
|
try {
|
||||||
|
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
|
||||||
if (createRoutineBloc.selectedSpaceId == '' &&
|
|
||||||
createRoutineBloc.selectedCommunityId == '') {
|
|
||||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||||
for (var communityId in spaceBloc.state.selectedCommunities) {
|
for (var communityId in spaceBloc.state.selectedCommunities) {
|
||||||
List<String> spacesList =
|
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||||
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
|
||||||
for (var spaceId in spacesList) {
|
for (var spaceId in spacesList) {
|
||||||
automations.addAll(
|
automations.addAll(await SceneApi.getAutomation(spaceId, communityId, projectId));
|
||||||
await SceneApi.getAutomation(spaceId, communityId, projectId));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
automations.addAll(await SceneApi.getAutomation(
|
automations.addAll(await SceneApi.getAutomation(
|
||||||
createRoutineBloc.selectedSpaceId,
|
createRoutineBloc.selectedSpaceId, createRoutineBloc.selectedCommunityId, projectId));
|
||||||
createRoutineBloc.selectedCommunityId,
|
|
||||||
projectId));
|
|
||||||
}
|
}
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
automations: automations,
|
automations: automations,
|
||||||
@ -253,16 +233,14 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onSearchRoutines(
|
FutureOr<void> _onSearchRoutines(SearchRoutines event, Emitter<RoutineState> emit) async {
|
||||||
SearchRoutines event, Emitter<RoutineState> emit) async {
|
|
||||||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
emit(state.copyWith(isLoading: false, errorMessage: null));
|
emit(state.copyWith(isLoading: false, errorMessage: null));
|
||||||
emit(state.copyWith(searchText: event.query));
|
emit(state.copyWith(searchText: event.query));
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onAddSelectedIcon(
|
FutureOr<void> _onAddSelectedIcon(AddSelectedIcon event, Emitter<RoutineState> emit) {
|
||||||
AddSelectedIcon event, Emitter<RoutineState> emit) {
|
|
||||||
emit(state.copyWith(selectedIcon: event.icon));
|
emit(state.copyWith(selectedIcon: event.icon));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,8 +254,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
return actions.last['deviceId'] == 'delay';
|
return actions.last['deviceId'] == 'delay';
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onCreateScene(
|
Future<void> _onCreateScene(CreateSceneEvent event, Emitter<RoutineState> emit) async {
|
||||||
CreateSceneEvent event, Emitter<RoutineState> emit) async {
|
|
||||||
try {
|
try {
|
||||||
// Check if first action is delay
|
// Check if first action is delay
|
||||||
// if (_isFirstActionDelay(state.thenItems)) {
|
// if (_isFirstActionDelay(state.thenItems)) {
|
||||||
@ -290,8 +267,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
|
|
||||||
if (_isLastActionDelay(state.thenItems)) {
|
if (_isLastActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
errorMessage:
|
errorMessage: 'A delay condition cannot be the only or the last action',
|
||||||
'A delay condition cannot be the only or the last action',
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
));
|
));
|
||||||
return;
|
return;
|
||||||
@ -367,8 +343,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onCreateAutomation(
|
Future<void> _onCreateAutomation(CreateAutomationEvent event, Emitter<RoutineState> emit) async {
|
||||||
CreateAutomationEvent event, Emitter<RoutineState> emit) async {
|
|
||||||
try {
|
try {
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
if (state.routineName == null || state.routineName!.isEmpty) {
|
if (state.routineName == null || state.routineName!.isEmpty) {
|
||||||
@ -390,8 +365,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
|
|
||||||
if (_isLastActionDelay(state.thenItems)) {
|
if (_isLastActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
errorMessage:
|
errorMessage: 'A delay condition cannot be the only or the last action',
|
||||||
'A delay condition cannot be the only or the last action',
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
));
|
));
|
||||||
CustomSnackBar.redSnackBar('Cannot have delay as the last action');
|
CustomSnackBar.redSnackBar('Cannot have delay as the last action');
|
||||||
@ -482,8 +456,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
actions: actions,
|
actions: actions,
|
||||||
);
|
);
|
||||||
|
|
||||||
final result =
|
final result = await SceneApi.createAutomation(createAutomationModel, projectUuid);
|
||||||
await SceneApi.createAutomation(createAutomationModel, projectUuid);
|
|
||||||
if (result['success']) {
|
if (result['success']) {
|
||||||
add(ResetRoutineState());
|
add(ResetRoutineState());
|
||||||
add(const LoadAutomation());
|
add(const LoadAutomation());
|
||||||
@ -504,21 +477,17 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onRemoveDragCard(
|
FutureOr<void> _onRemoveDragCard(RemoveDragCard event, Emitter<RoutineState> emit) {
|
||||||
RemoveDragCard event, Emitter<RoutineState> emit) {
|
|
||||||
if (event.isFromThen) {
|
if (event.isFromThen) {
|
||||||
final thenItems = List<Map<String, dynamic>>.from(state.thenItems);
|
final thenItems = List<Map<String, dynamic>>.from(state.thenItems);
|
||||||
final selectedFunctions =
|
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
||||||
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
|
||||||
|
|
||||||
thenItems.removeAt(event.index);
|
thenItems.removeAt(event.index);
|
||||||
selectedFunctions.remove(event.key);
|
selectedFunctions.remove(event.key);
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(thenItems: thenItems, selectedFunctions: selectedFunctions));
|
||||||
thenItems: thenItems, selectedFunctions: selectedFunctions));
|
|
||||||
} else {
|
} else {
|
||||||
final ifItems = List<Map<String, dynamic>>.from(state.ifItems);
|
final ifItems = List<Map<String, dynamic>>.from(state.ifItems);
|
||||||
final selectedFunctions =
|
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
||||||
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
|
||||||
|
|
||||||
ifItems.removeAt(event.index);
|
ifItems.removeAt(event.index);
|
||||||
selectedFunctions.remove(event.key);
|
selectedFunctions.remove(event.key);
|
||||||
@ -529,8 +498,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
isAutomation: false,
|
isAutomation: false,
|
||||||
isTabToRun: false));
|
isTabToRun: false));
|
||||||
} else {
|
} else {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(ifItems: ifItems, selectedFunctions: selectedFunctions));
|
||||||
ifItems: ifItems, selectedFunctions: selectedFunctions));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -542,141 +510,138 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onEffectiveTimeEvent(
|
FutureOr<void> _onEffectiveTimeEvent(EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
|
||||||
EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
|
|
||||||
emit(state.copyWith(effectiveTime: event.effectiveTime));
|
emit(state.copyWith(effectiveTime: event.effectiveTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onSetRoutineName(
|
FutureOr<void> _onSetRoutineName(SetRoutineName event, Emitter<RoutineState> emit) {
|
||||||
SetRoutineName event, Emitter<RoutineState> emit) {
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
routineName: event.name,
|
routineName: event.name,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
// (
|
||||||
List<Map<String, dynamic>>,
|
// List<Map<String, dynamic>>,
|
||||||
List<Map<String, dynamic>>,
|
// List<Map<String, dynamic>>,
|
||||||
Map<String, List<DeviceFunctionData>>
|
// Map<String, List<DeviceFunctionData>>
|
||||||
) _createCardData(
|
// ) _createCardData(
|
||||||
List<RoutineAction> actions,
|
// List<RoutineAction> actions,
|
||||||
List<RoutineCondition>? conditions,
|
// List<RoutineCondition>? conditions,
|
||||||
Map<String, List<DeviceFunctionData>> currentFunctions,
|
// Map<String, List<DeviceFunctionData>> currentFunctions,
|
||||||
bool isTabToRun,
|
// bool isTabToRun,
|
||||||
) {
|
// ) {
|
||||||
final ifItems = isTabToRun
|
// final ifItems = isTabToRun
|
||||||
? [
|
// ? [
|
||||||
{
|
// {
|
||||||
'entityId': 'tab_to_run',
|
// 'entityId': 'tab_to_run',
|
||||||
'uniqueCustomId': const Uuid().v4(),
|
// 'uniqueCustomId': const Uuid().v4(),
|
||||||
'deviceId': 'tab_to_run',
|
// 'deviceId': 'tab_to_run',
|
||||||
'title': 'Tab to run',
|
// 'title': 'Tab to run',
|
||||||
'productType': 'tab_to_run',
|
// 'productType': 'tab_to_run',
|
||||||
'imagePath': Assets.tabToRun,
|
// 'imagePath': Assets.tabToRun,
|
||||||
}
|
// }
|
||||||
]
|
// ]
|
||||||
: conditions?.map((condition) {
|
// : conditions?.map((condition) {
|
||||||
final matchingDevice = state.devices.firstWhere(
|
// final matchingDevice = state.devices.firstWhere(
|
||||||
(device) => device.uuid == condition.entityId,
|
// (device) => device.uuid == condition.entityId,
|
||||||
orElse: () => AllDevicesModel(
|
// orElse: () => AllDevicesModel(
|
||||||
uuid: condition.entityId,
|
// uuid: condition.entityId,
|
||||||
name: condition.entityId,
|
// name: condition.entityId,
|
||||||
productType: condition.entityType,
|
// productType: condition.entityType,
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
|
|
||||||
final cardData = {
|
// final cardData = {
|
||||||
'entityId': condition.entityId,
|
// 'entityId': condition.entityId,
|
||||||
'uniqueCustomId': const Uuid().v4(),
|
// 'uniqueCustomId': const Uuid().v4(),
|
||||||
'deviceId': condition.entityId,
|
// 'deviceId': condition.entityId,
|
||||||
'title': matchingDevice.name ?? condition.entityId,
|
// 'title': matchingDevice.name ?? condition.entityId,
|
||||||
'productType': condition.entityType,
|
// 'productType': condition.entityType,
|
||||||
'imagePath':
|
// 'imagePath':
|
||||||
matchingDevice.getDefaultIcon(condition.entityType),
|
// matchingDevice.getDefaultIcon(condition.entityType),
|
||||||
};
|
// };
|
||||||
|
|
||||||
final functions = matchingDevice.functions;
|
// final functions = matchingDevice.functions;
|
||||||
|
|
||||||
for (var function in functions) {
|
// for (var function in functions) {
|
||||||
if (function.code == condition.expr.statusCode) {
|
// if (function.code == condition.expr.statusCode) {
|
||||||
currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
|
// currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
|
||||||
DeviceFunctionData(
|
// DeviceFunctionData(
|
||||||
entityId: condition.entityId,
|
// entityId: condition.entityId,
|
||||||
functionCode: condition.expr.statusCode,
|
// functionCode: condition.expr.statusCode,
|
||||||
value: condition.expr.statusValue,
|
// value: condition.expr.statusValue,
|
||||||
operationName: function.operationName,
|
// operationName: function.operationName,
|
||||||
),
|
// ),
|
||||||
];
|
// ];
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
return cardData;
|
// return cardData;
|
||||||
}).toList() ??
|
// }).toList() ??
|
||||||
[];
|
// [];
|
||||||
|
|
||||||
final thenItems = actions.map((action) {
|
// final thenItems = actions.map((action) {
|
||||||
final matchingDevice = state.devices.firstWhere(
|
// final matchingDevice = state.devices.firstWhere(
|
||||||
(device) => device.uuid == action.entityId,
|
// (device) => device.uuid == action.entityId,
|
||||||
orElse: () => AllDevicesModel(
|
// orElse: () => AllDevicesModel(
|
||||||
uuid: action.entityId,
|
// uuid: action.entityId,
|
||||||
name: action.entityId,
|
// name: action.entityId,
|
||||||
productType: action.productType,
|
// productType: action.productType,
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
|
|
||||||
final cardData = {
|
// final cardData = {
|
||||||
'entityId': action.entityId,
|
// 'entityId': action.entityId,
|
||||||
'uniqueCustomId': const Uuid().v4(),
|
// 'uniqueCustomId': const Uuid().v4(),
|
||||||
'deviceId':
|
// 'deviceId':
|
||||||
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
// action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
||||||
'title': action.actionExecutor == 'delay'
|
// 'title': action.actionExecutor == 'delay'
|
||||||
? 'Delay'
|
// ? 'Delay'
|
||||||
: (matchingDevice.name ?? 'Device'),
|
// : (matchingDevice.name ?? 'Device'),
|
||||||
'productType': action.productType,
|
// 'productType': action.productType,
|
||||||
'imagePath': matchingDevice.getDefaultIcon(action.productType),
|
// 'imagePath': matchingDevice.getDefaultIcon(action.productType),
|
||||||
};
|
// };
|
||||||
|
|
||||||
final functions = matchingDevice.functions;
|
// final functions = matchingDevice.functions;
|
||||||
|
|
||||||
if (action.executorProperty != null && action.actionExecutor != 'delay') {
|
// if (action.executorProperty != null && action.actionExecutor != 'delay') {
|
||||||
final functionCode = action.executorProperty!.functionCode;
|
// final functionCode = action.executorProperty!.functionCode;
|
||||||
for (var function in functions) {
|
// for (var function in functions) {
|
||||||
if (function.code == functionCode) {
|
// if (function.code == functionCode) {
|
||||||
currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
|
// currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
|
||||||
DeviceFunctionData(
|
// DeviceFunctionData(
|
||||||
entityId: action.entityId,
|
// entityId: action.entityId,
|
||||||
functionCode: functionCode ?? '',
|
// functionCode: functionCode ?? '',
|
||||||
value: action.executorProperty!.functionValue,
|
// value: action.executorProperty!.functionValue,
|
||||||
operationName: function.operationName,
|
// operationName: function.operationName,
|
||||||
),
|
// ),
|
||||||
];
|
// ];
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
} else if (action.actionExecutor == 'delay') {
|
// } else if (action.actionExecutor == 'delay') {
|
||||||
final delayFunction = DelayFunction(
|
// final delayFunction = DelayFunction(
|
||||||
deviceId: action.entityId,
|
// deviceId: action.entityId,
|
||||||
deviceName: 'Delay',
|
// deviceName: 'Delay',
|
||||||
);
|
// );
|
||||||
currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
|
// currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
|
||||||
DeviceFunctionData(
|
// DeviceFunctionData(
|
||||||
entityId: action.entityId,
|
// entityId: action.entityId,
|
||||||
functionCode: 'delay',
|
// functionCode: 'delay',
|
||||||
value: action.executorProperty?.delaySeconds ?? 0,
|
// value: action.executorProperty?.delaySeconds ?? 0,
|
||||||
operationName: delayFunction.operationName,
|
// operationName: delayFunction.operationName,
|
||||||
),
|
// ),
|
||||||
];
|
// ];
|
||||||
}
|
// }
|
||||||
|
|
||||||
return cardData;
|
// return cardData;
|
||||||
}).toList();
|
// }).toList();
|
||||||
|
|
||||||
return (thenItems, ifItems, currentFunctions);
|
// return (thenItems, ifItems, currentFunctions);
|
||||||
}
|
// }
|
||||||
|
|
||||||
Future<void> _onGetSceneDetails(
|
Future<void> _onGetSceneDetails(GetSceneDetails event, Emitter<RoutineState> emit) async {
|
||||||
GetSceneDetails event, Emitter<RoutineState> emit) async {
|
|
||||||
try {
|
try {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
@ -719,17 +684,15 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
? '${action.entityId}_automation'
|
? '${action.entityId}_automation'
|
||||||
: action.actionExecutor == 'delay'
|
: action.actionExecutor == 'delay'
|
||||||
? '${action.entityId}_delay'
|
? '${action.entityId}_delay'
|
||||||
: action.entityId;
|
: const Uuid().v4();
|
||||||
|
|
||||||
if (!deviceCards.containsKey(deviceId)) {
|
// if (!deviceCards.containsKey(deviceId)) {
|
||||||
deviceCards[deviceId] = {
|
deviceCards[deviceId] = {
|
||||||
'entityId': action.entityId,
|
'entityId': action.entityId,
|
||||||
'deviceId':
|
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
||||||
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
'uniqueCustomId': action.type == 'automation' || action.actionExecutor == 'delay'
|
||||||
'uniqueCustomId':
|
? action.entityId
|
||||||
action.type == 'automation' || action.actionExecutor == 'delay'
|
: const Uuid().v4(),
|
||||||
? const Uuid().v4()
|
|
||||||
: action.entityId,
|
|
||||||
'title': action.actionExecutor == 'delay'
|
'title': action.actionExecutor == 'delay'
|
||||||
? 'Delay'
|
? 'Delay'
|
||||||
: action.type == 'automation'
|
: action.type == 'automation'
|
||||||
@ -745,16 +708,21 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
'device': matchingDevice,
|
'device': matchingDevice,
|
||||||
'name': action.name,
|
'name': action.name,
|
||||||
'type': action.type,
|
'type': action.type,
|
||||||
|
'tag': matchingDevice?.deviceTags?.isNotEmpty ?? false
|
||||||
|
? matchingDevice?.deviceTags![0].name ?? ''
|
||||||
|
: '',
|
||||||
|
'subSpace': matchingDevice?.deviceSubSpace?.subspaceName ?? '',
|
||||||
};
|
};
|
||||||
}
|
// }
|
||||||
|
|
||||||
final cardData = deviceCards[deviceId]!;
|
final cardData = deviceCards[deviceId]!;
|
||||||
final uniqueCustomId = cardData['uniqueCustomId'].toString();
|
final uniqueCustomId = cardData['uniqueCustomId'].toString();
|
||||||
|
|
||||||
if (action.type == 'automation') {
|
|
||||||
if (!updatedFunctions.containsKey(uniqueCustomId)) {
|
if (!updatedFunctions.containsKey(uniqueCustomId)) {
|
||||||
updatedFunctions[uniqueCustomId] = [];
|
updatedFunctions[uniqueCustomId] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action.type == 'automation') {
|
||||||
updatedFunctions[uniqueCustomId]!.add(
|
updatedFunctions[uniqueCustomId]!.add(
|
||||||
DeviceFunctionData(
|
DeviceFunctionData(
|
||||||
entityId: action.entityId,
|
entityId: action.entityId,
|
||||||
@ -764,14 +732,10 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
// emit(state.copyWith(automationActionExecutor: action.actionExecutor));
|
// emit(state.copyWith(automationActionExecutor: action.actionExecutor));
|
||||||
} else if (action.executorProperty != null &&
|
} else if (action.executorProperty != null && action.actionExecutor != 'delay') {
|
||||||
action.actionExecutor != 'delay') {
|
final functions = matchingDevice?.functions ?? [];
|
||||||
if (!updatedFunctions.containsKey(uniqueCustomId)) {
|
|
||||||
updatedFunctions[uniqueCustomId] = [];
|
|
||||||
}
|
|
||||||
final functions = matchingDevice?.functions;
|
|
||||||
final functionCode = action.executorProperty?.functionCode;
|
final functionCode = action.executorProperty?.functionCode;
|
||||||
for (DeviceFunction function in functions ?? []) {
|
for (DeviceFunction function in functions) {
|
||||||
if (function.code == functionCode) {
|
if (function.code == functionCode) {
|
||||||
updatedFunctions[uniqueCustomId]!.add(
|
updatedFunctions[uniqueCustomId]!.add(
|
||||||
DeviceFunctionData(
|
DeviceFunctionData(
|
||||||
@ -785,9 +749,6 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (action.actionExecutor == 'delay') {
|
} else if (action.actionExecutor == 'delay') {
|
||||||
if (!updatedFunctions.containsKey(uniqueCustomId)) {
|
|
||||||
updatedFunctions[uniqueCustomId] = [];
|
|
||||||
}
|
|
||||||
final delayFunction = DelayFunction(
|
final delayFunction = DelayFunction(
|
||||||
deviceId: action.entityId,
|
deviceId: action.entityId,
|
||||||
deviceName: 'Delay',
|
deviceName: 'Delay',
|
||||||
@ -837,8 +798,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onResetRoutineState(
|
FutureOr<void> _onResetRoutineState(ResetRoutineState event, Emitter<RoutineState> emit) {
|
||||||
ResetRoutineState event, Emitter<RoutineState> emit) {
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
ifItems: [],
|
ifItems: [],
|
||||||
thenItems: [],
|
thenItems: [],
|
||||||
@ -861,6 +821,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
isUpdate: false,
|
isUpdate: false,
|
||||||
createRoutineView: false));
|
createRoutineView: false));
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _deleteScene(DeleteScene event, Emitter<RoutineState> emit) async {
|
FutureOr<void> _deleteScene(DeleteScene event, Emitter<RoutineState> emit) async {
|
||||||
try {
|
try {
|
||||||
final projectId = await ProjectManager.getProjectUUID() ?? '';
|
final projectId = await ProjectManager.getProjectUUID() ?? '';
|
||||||
@ -915,8 +876,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
FutureOr<void> _fetchDevices(
|
FutureOr<void> _fetchDevices(FetchDevicesInRoutine event, Emitter<RoutineState> emit) async {
|
||||||
FetchDevicesInRoutine event, Emitter<RoutineState> emit) async {
|
|
||||||
emit(state.copyWith(isLoading: true));
|
emit(state.copyWith(isLoading: true));
|
||||||
try {
|
try {
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
@ -925,21 +885,17 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
||||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||||
|
|
||||||
if (createRoutineBloc.selectedSpaceId == '' &&
|
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
|
||||||
createRoutineBloc.selectedCommunityId == '') {
|
|
||||||
for (var communityId in spaceBloc.state.selectedCommunities) {
|
for (var communityId in spaceBloc.state.selectedCommunities) {
|
||||||
List<String> spacesList =
|
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||||
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
|
||||||
for (var spaceId in spacesList) {
|
for (var spaceId in spacesList) {
|
||||||
devices.addAll(await DevicesManagementApi()
|
devices.addAll(
|
||||||
.fetchDevices(communityId, spaceId, projectUuid));
|
await DevicesManagementApi().fetchDevices(communityId, spaceId, projectUuid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
devices.addAll(await DevicesManagementApi().fetchDevices(
|
devices.addAll(await DevicesManagementApi().fetchDevices(
|
||||||
createRoutineBloc.selectedCommunityId,
|
createRoutineBloc.selectedCommunityId, createRoutineBloc.selectedSpaceId, projectUuid));
|
||||||
createRoutineBloc.selectedSpaceId,
|
|
||||||
projectUuid));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(state.copyWith(isLoading: false, devices: devices));
|
emit(state.copyWith(isLoading: false, devices: devices));
|
||||||
@ -948,8 +904,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onUpdateScene(
|
FutureOr<void> _onUpdateScene(UpdateScene event, Emitter<RoutineState> emit) async {
|
||||||
UpdateScene event, Emitter<RoutineState> emit) async {
|
|
||||||
try {
|
try {
|
||||||
// Check if first action is delay
|
// Check if first action is delay
|
||||||
// if (_isFirstActionDelay(state.thenItems)) {
|
// if (_isFirstActionDelay(state.thenItems)) {
|
||||||
@ -963,8 +918,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
|
|
||||||
if (_isLastActionDelay(state.thenItems)) {
|
if (_isLastActionDelay(state.thenItems)) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
errorMessage:
|
errorMessage: 'A delay condition cannot be the only or the last action',
|
||||||
'A delay condition cannot be the only or the last action',
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
));
|
));
|
||||||
return;
|
return;
|
||||||
@ -1017,8 +971,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
actions: actions,
|
actions: actions,
|
||||||
);
|
);
|
||||||
|
|
||||||
final result =
|
final result = await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
|
||||||
await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
|
|
||||||
if (result['success']) {
|
if (result['success']) {
|
||||||
add(ResetRoutineState());
|
add(ResetRoutineState());
|
||||||
add(const LoadScenes());
|
add(const LoadScenes());
|
||||||
@ -1037,8 +990,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onUpdateAutomation(
|
FutureOr<void> _onUpdateAutomation(UpdateAutomation event, Emitter<RoutineState> emit) async {
|
||||||
UpdateAutomation event, Emitter<RoutineState> emit) async {
|
|
||||||
try {
|
try {
|
||||||
if (state.routineName == null || state.routineName!.isEmpty) {
|
if (state.routineName == null || state.routineName!.isEmpty) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
@ -1203,9 +1155,9 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final deviceId = condition.entityId;
|
final deviceId = const Uuid().v4();
|
||||||
|
|
||||||
if (!deviceIfCards.containsKey(deviceId)) {
|
// if (!deviceIfCards.containsKey(deviceId)) {
|
||||||
deviceIfCards[deviceId] = {
|
deviceIfCards[deviceId] = {
|
||||||
'entityId': condition.entityId,
|
'entityId': condition.entityId,
|
||||||
'deviceId': condition.entityId,
|
'deviceId': condition.entityId,
|
||||||
@ -1216,8 +1168,12 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
'imagePath': matchingDevice.getDefaultIcon(condition.productType),
|
'imagePath': matchingDevice.getDefaultIcon(condition.productType),
|
||||||
'device': matchingDevice,
|
'device': matchingDevice,
|
||||||
'type': 'condition',
|
'type': 'condition',
|
||||||
|
'tag': matchingDevice.deviceTags?.isNotEmpty ?? false
|
||||||
|
? matchingDevice.deviceTags![0].name
|
||||||
|
: '',
|
||||||
|
'subSpace': matchingDevice.deviceSubSpace?.subspaceName ?? '',
|
||||||
};
|
};
|
||||||
}
|
// }
|
||||||
|
|
||||||
final cardData = deviceIfCards[deviceId]!;
|
final cardData = deviceIfCards[deviceId]!;
|
||||||
final uniqueCustomId = cardData['uniqueCustomId'].toString();
|
final uniqueCustomId = cardData['uniqueCustomId'].toString();
|
||||||
@ -1253,15 +1209,12 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final deviceId = action.actionExecutor == 'delay'
|
final deviceId = const Uuid().v4();
|
||||||
? '${action.entityId}_delay'
|
|
||||||
: action.entityId;
|
|
||||||
|
|
||||||
if (!deviceThenCards.containsKey(deviceId)) {
|
// if (!deviceThenCards.containsKey(deviceId)) {
|
||||||
deviceThenCards[deviceId] = {
|
deviceThenCards[deviceId] = {
|
||||||
'entityId': action.entityId,
|
'entityId': action.entityId,
|
||||||
'deviceId':
|
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
||||||
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
|
||||||
'uniqueCustomId': const Uuid().v4(),
|
'uniqueCustomId': const Uuid().v4(),
|
||||||
'title': action.actionExecutor == 'delay'
|
'title': action.actionExecutor == 'delay'
|
||||||
? 'Delay'
|
? 'Delay'
|
||||||
@ -1281,9 +1234,13 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
: action.type == 'automation'
|
: action.type == 'automation'
|
||||||
? 'automation'
|
? 'automation'
|
||||||
: 'action',
|
: 'action',
|
||||||
'icon': action.icon ?? ''
|
'icon': action.icon ?? '',
|
||||||
|
'tag': matchingDevice.deviceTags?.isNotEmpty ?? false
|
||||||
|
? matchingDevice.deviceTags![0].name
|
||||||
|
: '',
|
||||||
|
'subSpace': matchingDevice.deviceSubSpace?.subspaceName ?? '',
|
||||||
};
|
};
|
||||||
}
|
// }
|
||||||
|
|
||||||
final cardData = deviceThenCards[deviceId]!;
|
final cardData = deviceThenCards[deviceId]!;
|
||||||
final uniqueCustomId = cardData['uniqueCustomId'].toString();
|
final uniqueCustomId = cardData['uniqueCustomId'].toString();
|
||||||
@ -1292,8 +1249,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
updatedFunctions[uniqueCustomId] = [];
|
updatedFunctions[uniqueCustomId] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.executorProperty != null &&
|
if (action.executorProperty != null && action.actionExecutor != 'delay') {
|
||||||
action.actionExecutor != 'delay') {
|
|
||||||
final functions = matchingDevice.functions;
|
final functions = matchingDevice.functions;
|
||||||
final functionCode = action.executorProperty!.functionCode;
|
final functionCode = action.executorProperty!.functionCode;
|
||||||
for (var function in functions) {
|
for (var function in functions) {
|
||||||
@ -1335,14 +1291,10 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final ifItems = deviceIfCards.values
|
final ifItems = deviceIfCards.values.where((card) => card['type'] == 'condition').toList();
|
||||||
.where((card) => card['type'] == 'condition')
|
|
||||||
.toList();
|
|
||||||
final thenItems = deviceThenCards.values
|
final thenItems = deviceThenCards.values
|
||||||
.where((card) =>
|
.where((card) =>
|
||||||
card['type'] == 'action' ||
|
card['type'] == 'action' || card['type'] == 'automation' || card['type'] == 'scene')
|
||||||
card['type'] == 'automation' ||
|
|
||||||
card['type'] == 'scene')
|
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
@ -1364,8 +1316,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onSceneTrigger(
|
Future<void> _onSceneTrigger(SceneTrigger event, Emitter<RoutineState> emit) async {
|
||||||
SceneTrigger event, Emitter<RoutineState> emit) async {
|
|
||||||
emit(state.copyWith(loadingSceneId: event.sceneId));
|
emit(state.copyWith(loadingSceneId: event.sceneId));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1407,29 +1358,24 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
|||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
final updatedAutomations = await SceneApi.getAutomationByUnitId(
|
final updatedAutomations = await SceneApi.getAutomationByUnitId(
|
||||||
event.automationStatusUpdate.spaceUuid,
|
event.automationStatusUpdate.spaceUuid, event.communityId, projectId);
|
||||||
event.communityId,
|
|
||||||
projectId);
|
|
||||||
|
|
||||||
// Remove from loading set safely
|
// Remove from loading set safely
|
||||||
final updatedLoadingIds = {...state.loadingAutomationIds!}
|
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
|
||||||
..remove(event.automationId);
|
|
||||||
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
automations: updatedAutomations,
|
automations: updatedAutomations,
|
||||||
loadingAutomationIds: updatedLoadingIds,
|
loadingAutomationIds: updatedLoadingIds,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
final updatedLoadingIds = {...state.loadingAutomationIds!}
|
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
|
||||||
..remove(event.automationId);
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
loadingAutomationIds: updatedLoadingIds,
|
loadingAutomationIds: updatedLoadingIds,
|
||||||
errorMessage: 'Update failed',
|
errorMessage: 'Update failed',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final updatedLoadingIds = {...state.loadingAutomationIds!}
|
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
|
||||||
..remove(event.automationId);
|
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
loadingAutomationIds: updatedLoadingIds,
|
loadingAutomationIds: updatedLoadingIds,
|
||||||
errorMessage: 'Update error: ${e.toString()}',
|
errorMessage: 'Update error: ${e.toString()}',
|
||||||
|
@ -63,7 +63,11 @@ class _CreateNewRoutinesDialogState extends State<CreateNewRoutinesDialog> {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(left: 15, right: 15),
|
padding: const EdgeInsets.only(left: 15, right: 15),
|
||||||
child: CommunityDropdown(
|
child: CommunityDropdown(
|
||||||
communities: _bloc.communities,
|
communities: _bloc.communities..sort(
|
||||||
|
(a, b) => a.name.toLowerCase().compareTo(
|
||||||
|
b.name.toLowerCase(),
|
||||||
|
),
|
||||||
|
),
|
||||||
selectedValue: _selectedCommunity,
|
selectedValue: _selectedCommunity,
|
||||||
onChanged: (String? newValue) {
|
onChanged: (String? newValue) {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
@ -14,13 +14,18 @@ class SaveRoutineHelper {
|
|||||||
static Future<void> showSaveRoutineDialog(BuildContext context) async {
|
static Future<void> showSaveRoutineDialog(BuildContext context) async {
|
||||||
return showDialog<void>(
|
return showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (context) {
|
||||||
return BlocBuilder<RoutineBloc, RoutineState>(
|
return BlocBuilder<RoutineBloc, RoutineState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
|
final selectedConditionLabel = state.selectedAutomationOperator == 'and'
|
||||||
|
? 'All Conditions are met'
|
||||||
|
: 'Any Condition is met';
|
||||||
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
content: Container(
|
content: Container(
|
||||||
width: 600,
|
width: context.screenWidth * 0.5,
|
||||||
|
height: 500,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
@ -28,127 +33,89 @@ class SaveRoutineHelper {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
DialogHeader('Create a scene: ${state.routineName ?? ""}'),
|
const SizedBox(height: 18),
|
||||||
Padding(
|
Text(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
'Create a scene: ${state.routineName ?? ""}',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.headlineMedium!.copyWith(
|
||||||
|
color: ColorsManager.primaryColorWithOpacity,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 18),
|
||||||
|
_buildDivider(),
|
||||||
|
_buildListsLabelRow(selectedConditionLabel),
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsetsDirectional.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
spacing: 24,
|
||||||
children: [
|
children: [
|
||||||
// Left side - IF
|
_buildIfConditions(state, context),
|
||||||
Expanded(
|
Container(
|
||||||
child: Column(
|
width: 1,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
color: ColorsManager.greyColor.withValues(alpha: 0.8),
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'IF:',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
),
|
||||||
),
|
_buildThenActions(state, context),
|
||||||
const SizedBox(height: 8),
|
|
||||||
if (state.isTabToRun)
|
|
||||||
ListTile(
|
|
||||||
leading: SvgPicture.asset(
|
|
||||||
Assets.tabToRun,
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
),
|
|
||||||
title: const Text('Tab to run'),
|
|
||||||
),
|
|
||||||
if (state.isAutomation)
|
|
||||||
...state.ifItems.map((item) {
|
|
||||||
final functions =
|
|
||||||
state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
|
||||||
return ListTile(
|
|
||||||
leading: SvgPicture.asset(
|
|
||||||
item['imagePath'],
|
|
||||||
width: 22,
|
|
||||||
height: 22,
|
|
||||||
),
|
|
||||||
title:
|
|
||||||
Text(item['title'], style: const TextStyle(fontSize: 14)),
|
|
||||||
subtitle: Wrap(
|
|
||||||
children: functions
|
|
||||||
.map((f) => Text(
|
|
||||||
'${f.operationName}: ${f.value}, ',
|
|
||||||
style: const TextStyle(
|
|
||||||
color: ColorsManager.grayColor, fontSize: 8),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
maxLines: 3,
|
|
||||||
))
|
|
||||||
.toList(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
),
|
||||||
// Right side - THEN items
|
_buildDivider(),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_buildDialogFooter(context, state),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Expanded(
|
static Container _buildDivider() {
|
||||||
child: Column(
|
return Container(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
height: 1,
|
||||||
children: [
|
width: double.infinity,
|
||||||
const Text(
|
color: ColorsManager.greyColor,
|
||||||
'THEN:',
|
);
|
||||||
style: TextStyle(
|
}
|
||||||
|
|
||||||
|
static Widget _buildListsLabelRow(String selectedConditionLabel) {
|
||||||
|
const textStyle = TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
);
|
||||||
),
|
return Container(
|
||||||
const SizedBox(height: 8),
|
color: ColorsManager.backgroundColor.withValues(alpha: 0.5),
|
||||||
...state.thenItems.map((item) {
|
padding: const EdgeInsetsDirectional.all(20),
|
||||||
final functions =
|
child: Row(
|
||||||
state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
spacing: 16,
|
||||||
return ListTile(
|
children: [
|
||||||
leading: item['type'] == 'tap_to_run' || item['type'] == 'scene'
|
Expanded(child: Text('IF: $selectedConditionLabel', style: textStyle)),
|
||||||
? Image.memory(
|
const Expanded(child: Text('THEN:', style: textStyle)),
|
||||||
base64Decode(item['icon']),
|
],
|
||||||
width: 22,
|
|
||||||
height: 22,
|
|
||||||
)
|
|
||||||
: SvgPicture.asset(
|
|
||||||
item['imagePath'],
|
|
||||||
width: 22,
|
|
||||||
height: 22,
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
item['title'],
|
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
|
||||||
fontSize: 14,
|
|
||||||
color: ColorsManager.grayColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
subtitle: Wrap(
|
|
||||||
children: functions
|
|
||||||
.map((f) => Text(
|
|
||||||
'${f.operationName}: ${f.value}, ',
|
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
|
||||||
color: ColorsManager.grayColor, fontSize: 8),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
maxLines: 3,
|
|
||||||
))
|
|
||||||
.toList(),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}
|
||||||
],
|
|
||||||
|
static Widget _buildDialogFooter(BuildContext context, RoutineState state) {
|
||||||
|
return Row(
|
||||||
|
spacing: 16,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
DialogFooterButton(
|
||||||
|
text: 'Cancel',
|
||||||
|
onTap: () => Navigator.pop(context),
|
||||||
),
|
),
|
||||||
),
|
DialogFooterButton(
|
||||||
],
|
text: 'Confirm',
|
||||||
),
|
onTap: () {
|
||||||
),
|
|
||||||
// if (state.errorMessage != null || state.errorMessage!.isNotEmpty)
|
|
||||||
// Padding(
|
|
||||||
// padding: const EdgeInsets.all(8.0),
|
|
||||||
// child: Text(
|
|
||||||
// state.errorMessage!,
|
|
||||||
// style: const TextStyle(color: Colors.red),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
DialogFooter(
|
|
||||||
onCancel: () => Navigator.pop(context),
|
|
||||||
onConfirm: () async {
|
|
||||||
if (state.isAutomation) {
|
if (state.isAutomation) {
|
||||||
if (state.isUpdate ?? false) {
|
if (state.isUpdate ?? false) {
|
||||||
context.read<RoutineBloc>().add(const UpdateAutomation());
|
context.read<RoutineBloc>().add(const UpdateAutomation());
|
||||||
@ -162,19 +129,194 @@ class SaveRoutineHelper {
|
|||||||
context.read<RoutineBloc>().add(const CreateSceneEvent());
|
context.read<RoutineBloc>().add(const CreateSceneEvent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if (state.errorMessage == null || state.errorMessage!.isEmpty) {
|
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
isConfirmEnabled: true,
|
textColor: ColorsManager.primaryColorWithOpacity,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Widget _buildThenActions(RoutineState state, BuildContext context) {
|
||||||
|
return Expanded(
|
||||||
|
child: ListView(
|
||||||
|
// shrinkWrap: true,
|
||||||
|
children: state.thenItems.map((item) {
|
||||||
|
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
||||||
|
return functionRow(item, context, functions);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Widget _buildIfConditions(RoutineState state, BuildContext context) {
|
||||||
|
return Expanded(
|
||||||
|
child: ListView(
|
||||||
|
// shrinkWrap: true,
|
||||||
|
children: [
|
||||||
|
if (state.isTabToRun)
|
||||||
|
ListTile(
|
||||||
|
leading: SvgPicture.asset(
|
||||||
|
Assets.tabToRun,
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
title: const Text('Tab to run'),
|
||||||
|
),
|
||||||
|
if (state.isAutomation)
|
||||||
|
...state.ifItems.map((item) {
|
||||||
|
final functions =
|
||||||
|
state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
||||||
|
return functionRow(item, context, functions);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Widget functionRow(
|
||||||
|
dynamic item,
|
||||||
|
BuildContext context,
|
||||||
|
List<DeviceFunctionData> functions,
|
||||||
|
) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 6),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
spacing: 17,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 22,
|
||||||
|
height: 22,
|
||||||
|
padding: const EdgeInsetsDirectional.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: ColorsManager.textFieldGreyColor,
|
||||||
|
border: Border.all(
|
||||||
|
color: ColorsManager.neutralGray,
|
||||||
|
width: 1.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: item['type'] == 'tap_to_run' || item['type'] == 'scene'
|
||||||
|
? Image.memory(
|
||||||
|
base64Decode(item['icon']),
|
||||||
|
width: 12,
|
||||||
|
height: 22,
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
)
|
||||||
|
: SvgPicture.asset(
|
||||||
|
item['imagePath'],
|
||||||
|
width: 12,
|
||||||
|
height: 12,
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
spacing: 2,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
item['title'],
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
fontSize: 15,
|
||||||
|
color: ColorsManager.textPrimaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Wrap(
|
||||||
|
runSpacing: 16,
|
||||||
|
spacing: 4,
|
||||||
|
children: functions
|
||||||
|
.map(
|
||||||
|
(function) => Text(
|
||||||
|
'${function.operationName}: ${function.value}',
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
fontSize: 8,
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 3,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
],
|
||||||
},
|
),
|
||||||
);
|
),
|
||||||
},
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
spacing: 2,
|
||||||
|
children: [
|
||||||
|
Visibility(
|
||||||
|
visible: item['tag'] != null && item['tag'] != '',
|
||||||
|
child: Row(
|
||||||
|
spacing: 2,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.deviceTagIcon,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
item['tag'] ?? '',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: ColorsManager.lightGreyColor,
|
||||||
|
fontSize: 9,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: item['subSpace'] != null && item['subSpace'] != '',
|
||||||
|
child: Row(
|
||||||
|
spacing: 2,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.spaceLocationIcon,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
item['subSpace'] ?? '',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: ColorsManager.lightGreyColor,
|
||||||
|
fontSize: 9,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
// TODO: functions in conditions / status in actions
|
|
||||||
class CpsOperationalValue {
|
class CpsOperationalValue {
|
||||||
final String icon;
|
final String icon;
|
||||||
final String description;
|
final String description;
|
||||||
@ -223,7 +222,6 @@ final class CpsSpatialMotionValueFunction extends CpsFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class CpsMaxDistanceOfDetectionFunction extends CpsFunctions {
|
final class CpsMaxDistanceOfDetectionFunction extends CpsFunctions {
|
||||||
// confirm with BE
|
|
||||||
CpsMaxDistanceOfDetectionFunction({
|
CpsMaxDistanceOfDetectionFunction({
|
||||||
required super.deviceId,
|
required super.deviceId,
|
||||||
required super.deviceName,
|
required super.deviceName,
|
||||||
@ -267,7 +265,7 @@ final class CpsMaxDistanceOfStaticDetectionFunction extends CpsFunctions {
|
|||||||
max = 10.0,
|
max = 10.0,
|
||||||
step = 0.5,
|
step = 0.5,
|
||||||
super(
|
super(
|
||||||
code: 'static_max_dis', // 0 / 500
|
code: 'static_max_dis',
|
||||||
operationName: 'Maximum Distance Of Static Detection',
|
operationName: 'Maximum Distance Of Static Detection',
|
||||||
icon: Assets.currentDistanceIcon,
|
icon: Assets.currentDistanceIcon,
|
||||||
);
|
);
|
||||||
@ -302,7 +300,7 @@ final class CpsDetectionRangeFunction extends CpsFunctions {
|
|||||||
max = 25.5,
|
max = 25.5,
|
||||||
step = 0.1,
|
step = 0.1,
|
||||||
super(
|
super(
|
||||||
code: 'moving_range', // just then
|
code: 'moving_range',
|
||||||
operationName: 'Detection Range',
|
operationName: 'Detection Range',
|
||||||
icon: Assets.farDetection,
|
icon: Assets.farDetection,
|
||||||
);
|
);
|
||||||
@ -372,7 +370,7 @@ final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions {
|
|||||||
max = 255,
|
max = 255,
|
||||||
step = 5,
|
step = 5,
|
||||||
super(
|
super(
|
||||||
code: 'presence_reference', // max 255 // change widget
|
code: 'presence_reference',
|
||||||
operationName: 'Presence Judgement Threshold',
|
operationName: 'Presence Judgement Threshold',
|
||||||
icon: Assets.presenceJudgementThrshold,
|
icon: Assets.presenceJudgementThrshold,
|
||||||
);
|
);
|
||||||
@ -403,7 +401,7 @@ final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions {
|
|||||||
max = 255,
|
max = 255,
|
||||||
step = 5,
|
step = 5,
|
||||||
super(
|
super(
|
||||||
code: 'moving_reference', // max 255 // change widget
|
code: 'moving_reference',
|
||||||
operationName: 'Motion Amplitude Trigger Threshold',
|
operationName: 'Motion Amplitude Trigger Threshold',
|
||||||
icon: Assets.presenceJudgementThrshold,
|
icon: Assets.presenceJudgementThrshold,
|
||||||
);
|
);
|
||||||
@ -434,7 +432,7 @@ final class CpsPerpetualBoundaryFunction extends CpsFunctions {
|
|||||||
max = 5.00,
|
max = 5.00,
|
||||||
step = 0.50,
|
step = 0.50,
|
||||||
super(
|
super(
|
||||||
code: 'perceptual_boundary', // 0 / 500
|
code: 'perceptual_boundary',
|
||||||
operationName: 'Perpetual Boundary',
|
operationName: 'Perpetual Boundary',
|
||||||
icon: Assets.boundary,
|
icon: Assets.boundary,
|
||||||
);
|
);
|
||||||
@ -469,7 +467,7 @@ final class CpsMotionTriggerBoundaryFunction extends CpsFunctions {
|
|||||||
max = 5.0,
|
max = 5.0,
|
||||||
step = 0.5,
|
step = 0.5,
|
||||||
super(
|
super(
|
||||||
code: 'moving_boundary', // 0 / 500 / step 50
|
code: 'moving_boundary',
|
||||||
operationName: 'Motion Trigger Boundary',
|
operationName: 'Motion Trigger Boundary',
|
||||||
icon: Assets.motionMeter,
|
icon: Assets.motionMeter,
|
||||||
);
|
);
|
||||||
@ -504,7 +502,7 @@ final class CpsMotionTriggerTimeFunction extends CpsFunctions {
|
|||||||
max = 2.0,
|
max = 2.0,
|
||||||
step = 0.1,
|
step = 0.1,
|
||||||
super(
|
super(
|
||||||
code: 'moving_rigger_time', // 0 / 2000 steps 10
|
code: 'moving_rigger_time',
|
||||||
operationName: 'Motion Trigger Time',
|
operationName: 'Motion Trigger Time',
|
||||||
icon: Assets.motionMeter,
|
icon: Assets.motionMeter,
|
||||||
);
|
);
|
||||||
@ -539,7 +537,7 @@ final class CpsMotionToStaticTimeFunction extends CpsFunctions {
|
|||||||
max = 50.0,
|
max = 50.0,
|
||||||
step = 1.0,
|
step = 1.0,
|
||||||
super(
|
super(
|
||||||
code: 'moving_static_time', // 0 / 6000 steps 100
|
code: 'moving_static_time',
|
||||||
operationName: 'Motion To Static Time',
|
operationName: 'Motion To Static Time',
|
||||||
icon: Assets.motionMeter,
|
icon: Assets.motionMeter,
|
||||||
);
|
);
|
||||||
@ -574,7 +572,7 @@ final class CpsEnteringNoBodyStateTimeFunction extends CpsFunctions {
|
|||||||
max = 300.0,
|
max = 300.0,
|
||||||
step = 5.0,
|
step = 5.0,
|
||||||
super(
|
super(
|
||||||
code: 'none_body_time', // 0 / 300000 / steps 500
|
code: 'none_body_time',
|
||||||
operationName: 'Entering Nobody State Time',
|
operationName: 'Entering Nobody State Time',
|
||||||
icon: Assets.motionMeter,
|
icon: Assets.motionMeter,
|
||||||
);
|
);
|
||||||
@ -606,7 +604,7 @@ final class CpsSelfTestResultFunctions extends CpsFunctions {
|
|||||||
required super.deviceName,
|
required super.deviceName,
|
||||||
required super.type,
|
required super.type,
|
||||||
}) : super(
|
}) : super(
|
||||||
code: 'checking_result', // just in action
|
code: 'checking_result',
|
||||||
operationName: 'Self-Test Result',
|
operationName: 'Self-Test Result',
|
||||||
icon: Assets.selfTestResult,
|
icon: Assets.selfTestResult,
|
||||||
);
|
);
|
||||||
@ -828,7 +826,7 @@ class CpsPresenceStatusFunctions extends CpsFunctions {
|
|||||||
required super.deviceName,
|
required super.deviceName,
|
||||||
required super.type,
|
required super.type,
|
||||||
}) : super(
|
}) : super(
|
||||||
code: 'presence_state', // just in action
|
code: 'presence_state',
|
||||||
operationName: 'Presence Status',
|
operationName: 'Presence Status',
|
||||||
icon: Assets.presenceSensor,
|
icon: Assets.presenceSensor,
|
||||||
);
|
);
|
||||||
@ -864,7 +862,7 @@ final class CpsSportsParaFunction extends CpsFunctions {
|
|||||||
max = 100,
|
max = 100,
|
||||||
step = 1,
|
step = 1,
|
||||||
super(
|
super(
|
||||||
code: 'sports_para', // just in action
|
code: 'sports_para',
|
||||||
operationName: 'Sports Para',
|
operationName: 'Sports Para',
|
||||||
icon: Assets.sportsPara,
|
icon: Assets.sportsPara,
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_event.dart';
|
|
||||||
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_event.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/create_new_routines/create_new_routines.dart';
|
import 'package:syncrow_web/pages/routines/create_new_routines/create_new_routines.dart';
|
||||||
import 'package:syncrow_web/pages/routines/view/create_new_routine_view.dart';
|
import 'package:syncrow_web/pages/routines/view/create_new_routine_view.dart';
|
||||||
@ -9,6 +9,7 @@ import 'package:syncrow_web/pages/routines/widgets/main_routine_view/fetch_routi
|
|||||||
import 'package:syncrow_web/pages/routines/widgets/main_routine_view/routine_view_card.dart';
|
import 'package:syncrow_web/pages/routines/widgets/main_routine_view/routine_view_card.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
class RoutinesView extends StatefulWidget {
|
class RoutinesView extends StatefulWidget {
|
||||||
const RoutinesView({super.key});
|
const RoutinesView({super.key});
|
||||||
@ -27,10 +28,10 @@ class _RoutinesViewState extends State<RoutinesView> {
|
|||||||
if (result == null) return;
|
if (result == null) return;
|
||||||
final communityId = result['community'];
|
final communityId = result['community'];
|
||||||
final spaceId = result['space'];
|
final spaceId = result['space'];
|
||||||
final _bloc = BlocProvider.of<CreateRoutineBloc>(context);
|
final bloc = BlocProvider.of<CreateRoutineBloc>(context);
|
||||||
final routineBloc = context.read<RoutineBloc>();
|
final routineBloc = context.read<RoutineBloc>();
|
||||||
_bloc.add(SaveCommunityIdAndSpaceIdEvent(
|
bloc.add(
|
||||||
communityID: communityId, spaceID: spaceId));
|
SaveCommunityIdAndSpaceIdEvent(communityID: communityId, spaceID: spaceId));
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
routineBloc.add(const CreateNewRoutineViewEvent(createRoutineView: true));
|
routineBloc.add(const CreateNewRoutineViewEvent(createRoutineView: true));
|
||||||
}
|
}
|
||||||
@ -49,28 +50,29 @@ class _RoutinesViewState extends State<RoutinesView> {
|
|||||||
child: SpaceTreeView(
|
child: SpaceTreeView(
|
||||||
onSelect: () => context.read<RoutineBloc>()
|
onSelect: () => context.read<RoutineBloc>()
|
||||||
..add(const LoadScenes())
|
..add(const LoadScenes())
|
||||||
..add(const LoadAutomation()),
|
..add(const LoadAutomation())
|
||||||
|
..add(FetchDevicesInRoutine()),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 4,
|
flex: 4,
|
||||||
child: ListView(
|
child: SizedBox(
|
||||||
children: [
|
height: context.screenHeight,
|
||||||
Container(
|
width: context.screenWidth,
|
||||||
padding: const EdgeInsets.all(16),
|
child: SingleChildScrollView(
|
||||||
height: MediaQuery.sizeOf(context).height,
|
padding: const EdgeInsetsDirectional.all(16),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
spacing: 16,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Create New Routines",
|
"Create New Routines",
|
||||||
style:
|
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||||
Theme.of(context).textTheme.titleLarge?.copyWith(
|
|
||||||
color: ColorsManager.grayColor,
|
color: ColorsManager.grayColor,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
|
||||||
RoutineViewCard(
|
RoutineViewCard(
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
onChanged: (v) {},
|
onChanged: (v) {},
|
||||||
@ -85,13 +87,10 @@ class _RoutinesViewState extends State<RoutinesView> {
|
|||||||
icon: Icons.add,
|
icon: Icons.add,
|
||||||
textString: '',
|
textString: '',
|
||||||
),
|
),
|
||||||
const SizedBox(height: 15),
|
const FetchRoutineScenesAutomation(),
|
||||||
const Expanded(child: FetchRoutineScenesAutomation()),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 50),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -8,12 +8,12 @@ class DialogFooter extends StatelessWidget {
|
|||||||
final int? dialogWidth;
|
final int? dialogWidth;
|
||||||
|
|
||||||
const DialogFooter({
|
const DialogFooter({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.onCancel,
|
required this.onCancel,
|
||||||
required this.onConfirm,
|
required this.onConfirm,
|
||||||
required this.isConfirmEnabled,
|
required this.isConfirmEnabled,
|
||||||
this.dialogWidth,
|
this.dialogWidth,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -28,45 +28,51 @@ class DialogFooter extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
DialogFooterButton(
|
||||||
child: _buildFooterButton(
|
text: 'Cancel',
|
||||||
context,
|
onTap: onCancel,
|
||||||
'Cancel',
|
|
||||||
onCancel,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (isConfirmEnabled) ...[
|
if (isConfirmEnabled) ...[
|
||||||
Container(width: 1, height: 50, color: ColorsManager.greyColor),
|
Container(width: 1, height: 50, color: ColorsManager.greyColor),
|
||||||
Expanded(
|
DialogFooterButton(
|
||||||
child: _buildFooterButton(
|
text: 'Confirm',
|
||||||
context,
|
onTap: onConfirm,
|
||||||
'Confirm',
|
textColor: isConfirmEnabled
|
||||||
onConfirm,
|
? ColorsManager.primaryColorWithOpacity
|
||||||
),
|
: Colors.red,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildFooterButton(
|
class DialogFooterButton extends StatelessWidget {
|
||||||
BuildContext context,
|
const DialogFooterButton({
|
||||||
String text,
|
required this.text,
|
||||||
VoidCallback? onTap,
|
required this.onTap,
|
||||||
) {
|
this.textColor,
|
||||||
return GestureDetector(
|
super.key,
|
||||||
onTap: onTap,
|
});
|
||||||
child: SizedBox(
|
|
||||||
height: 50,
|
final String text;
|
||||||
child: Center(
|
final VoidCallback? onTap;
|
||||||
|
final Color? textColor;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Expanded(
|
||||||
|
child: TextButton(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: ColorsManager.primaryColorWithOpacity,
|
||||||
|
disabledForegroundColor: ColorsManager.primaryColor,
|
||||||
|
),
|
||||||
|
onPressed: onTap,
|
||||||
child: Text(
|
child: Text(
|
||||||
text,
|
text,
|
||||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
color: text == 'Confirm'
|
color: textColor ?? ColorsManager.textGray,
|
||||||
? ColorsManager.primaryColorWithOpacity
|
|
||||||
: ColorsManager.textGray,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -16,6 +16,7 @@ class DialogHeader extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
color: ColorsManager.primaryColorWithOpacity,
|
color: ColorsManager.primaryColorWithOpacity,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
|
@ -6,6 +6,7 @@ import 'package:flutter_svg/flutter_svg.dart';
|
|||||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
class DraggableCard extends StatelessWidget {
|
class DraggableCard extends StatelessWidget {
|
||||||
@ -68,9 +69,9 @@ class DraggableCard extends StatelessWidget {
|
|||||||
Card(
|
Card(
|
||||||
color: ColorsManager.whiteColors,
|
color: ColorsManager.whiteColors,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: padding ?? const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
width: 110,
|
width: 110,
|
||||||
height: deviceFunctions.isEmpty ? 123 : null,
|
height: deviceFunctions.isEmpty ? 160 : 180,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@ -78,6 +79,7 @@ class DraggableCard extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Column(
|
Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
height: 50,
|
height: 50,
|
||||||
@ -112,8 +114,69 @@ class DraggableCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: deviceData['tag'] != null && deviceData['tag'] != '',
|
||||||
|
child: Row(
|
||||||
|
spacing: 2,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 8, height: 8, child: SvgPicture.asset(Assets.deviceTagIcon)),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
deviceData['tag'] ?? '',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: ColorsManager.lightGreyColor,
|
||||||
|
fontSize: 9,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: deviceData['subSpace'] != null && deviceData['subSpace'] != '',
|
||||||
|
child: const SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: deviceData['subSpace'] != null && deviceData['subSpace'] != '',
|
||||||
|
child: Row(
|
||||||
|
spacing: 2,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
child: SvgPicture.asset(Assets.spaceLocationIcon)),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
deviceData['subSpace'] ?? '',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: ColorsManager.lightGreyColor,
|
||||||
|
fontSize: 9,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (deviceFunctions.isNotEmpty)
|
||||||
|
const SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
if (deviceFunctions.isNotEmpty)
|
if (deviceFunctions.isNotEmpty)
|
||||||
...deviceFunctions.map((function) => Row(
|
...deviceFunctions.map((function) => Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@ -123,7 +186,7 @@ class DraggableCard extends StatelessWidget {
|
|||||||
'${function.operationName}: ${_formatFunctionValue(function)}',
|
'${function.operationName}: ${_formatFunctionValue(function)}',
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
fontSize: 9,
|
fontSize: 9,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.lightGreyColor,
|
||||||
height: 1.2,
|
height: 1.2,
|
||||||
),
|
),
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
@ -162,7 +225,7 @@ class DraggableCard extends StatelessWidget {
|
|||||||
if (function.functionCode == 'temp_set' || function.functionCode == 'temp_current') {
|
if (function.functionCode == 'temp_set' || function.functionCode == 'temp_current') {
|
||||||
return '${(function.value / 10).toStringAsFixed(0)}°C';
|
return '${(function.value / 10).toStringAsFixed(0)}°C';
|
||||||
} else if (function.functionCode.contains('countdown')) {
|
} else if (function.functionCode.contains('countdown')) {
|
||||||
final seconds = function.value.toInt();
|
final seconds = function.value?.toInt() ?? 0;
|
||||||
if (seconds >= 3600) {
|
if (seconds >= 3600) {
|
||||||
final hours = (seconds / 3600).floor();
|
final hours = (seconds / 3600).floor();
|
||||||
final remainingMinutes = ((seconds % 3600) / 60).floor();
|
final remainingMinutes = ((seconds % 3600) / 60).floor();
|
||||||
|
@ -17,7 +17,8 @@ class IfContainer extends StatelessWidget {
|
|||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return DragTarget<Map<String, dynamic>>(
|
return DragTarget<Map<String, dynamic>>(
|
||||||
builder: (context, candidateData, rejectedData) {
|
builder: (context, candidateData, rejectedData) {
|
||||||
return Container(
|
return SingleChildScrollView(
|
||||||
|
child: Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -27,8 +28,7 @@ class IfContainer extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
const Text('IF',
|
const Text('IF',
|
||||||
style:
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||||
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
|
||||||
if (state.isAutomation && state.ifItems.isNotEmpty)
|
if (state.isAutomation && state.ifItems.isNotEmpty)
|
||||||
AutomationOperatorSelector(
|
AutomationOperatorSelector(
|
||||||
selectedOperator: state.selectedAutomationOperator),
|
selectedOperator: state.selectedAutomationOperator),
|
||||||
@ -55,17 +55,16 @@ class IfContainer extends StatelessWidget {
|
|||||||
(index) => GestureDetector(
|
(index) => GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
if (!state.isTabToRun) {
|
if (!state.isTabToRun) {
|
||||||
final result =
|
final result = await DeviceDialogHelper.showDeviceDialog(
|
||||||
await DeviceDialogHelper.showDeviceDialog(
|
|
||||||
context: context,
|
context: context,
|
||||||
data: state.ifItems[index],
|
data: state.ifItems[index],
|
||||||
removeComparetors: false,
|
removeComparetors: false,
|
||||||
dialogType: "IF");
|
dialogType: "IF");
|
||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
context.read<RoutineBloc>().add(
|
context
|
||||||
AddToIfContainer(
|
.read<RoutineBloc>()
|
||||||
state.ifItems[index], false));
|
.add(AddToIfContainer(state.ifItems[index], false));
|
||||||
} else if (![
|
} else if (![
|
||||||
'AC',
|
'AC',
|
||||||
'1G',
|
'1G',
|
||||||
@ -74,11 +73,10 @@ class IfContainer extends StatelessWidget {
|
|||||||
'WPS',
|
'WPS',
|
||||||
'GW',
|
'GW',
|
||||||
'CPS',
|
'CPS',
|
||||||
].contains(
|
].contains(state.ifItems[index]['productType'])) {
|
||||||
state.ifItems[index]['productType'])) {
|
context
|
||||||
context.read<RoutineBloc>().add(
|
.read<RoutineBloc>()
|
||||||
AddToIfContainer(
|
.add(AddToIfContainer(state.ifItems[index], false));
|
||||||
state.ifItems[index], false));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -86,22 +84,21 @@ class IfContainer extends StatelessWidget {
|
|||||||
imagePath: state.ifItems[index]['imagePath'] ?? '',
|
imagePath: state.ifItems[index]['imagePath'] ?? '',
|
||||||
title: state.ifItems[index]['title'] ?? '',
|
title: state.ifItems[index]['title'] ?? '',
|
||||||
deviceData: state.ifItems[index],
|
deviceData: state.ifItems[index],
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
|
||||||
horizontal: 4, vertical: 8),
|
|
||||||
isFromThen: false,
|
isFromThen: false,
|
||||||
isFromIf: true,
|
isFromIf: true,
|
||||||
onRemove: () {
|
onRemove: () {
|
||||||
context.read<RoutineBloc>().add(RemoveDragCard(
|
context.read<RoutineBloc>().add(RemoveDragCard(
|
||||||
index: index,
|
index: index,
|
||||||
isFromThen: false,
|
isFromThen: false,
|
||||||
key: state.ifItems[index]
|
key: state.ifItems[index]['uniqueCustomId']));
|
||||||
['uniqueCustomId']));
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onAcceptWithDetails: (data) async {
|
onAcceptWithDetails: (data) async {
|
||||||
@ -124,14 +121,10 @@ class IfContainer extends StatelessWidget {
|
|||||||
removeComparetors: false);
|
removeComparetors: false);
|
||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
context
|
context.read<RoutineBloc>().add(AddToIfContainer(mutableData, false));
|
||||||
.read<RoutineBloc>()
|
|
||||||
.add(AddToIfContainer(mutableData, false));
|
|
||||||
} else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS']
|
} else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS']
|
||||||
.contains(mutableData['productType'])) {
|
.contains(mutableData['productType'])) {
|
||||||
context
|
context.read<RoutineBloc>().add(AddToIfContainer(mutableData, false));
|
||||||
.read<RoutineBloc>()
|
|
||||||
.add(AddToIfContainer(mutableData, false));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -177,9 +170,7 @@ class AutomationOperatorSelector extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context
|
context.read<RoutineBloc>().add(const ChangeAutomationOperator(operator: 'or'));
|
||||||
.read<RoutineBloc>()
|
|
||||||
.add(const ChangeAutomationOperator(operator: 'or'));
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
@ -205,9 +196,7 @@ class AutomationOperatorSelector extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context
|
context.read<RoutineBloc>().add(const ChangeAutomationOperator(operator: 'and'));
|
||||||
.read<RoutineBloc>()
|
|
||||||
.add(const ChangeAutomationOperator(operator: 'and'));
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -8,67 +8,121 @@ import 'package:syncrow_web/utils/constants/assets.dart';
|
|||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
class FetchRoutineScenesAutomation extends StatefulWidget {
|
class FetchRoutineScenesAutomation extends StatelessWidget
|
||||||
const FetchRoutineScenesAutomation({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<FetchRoutineScenesAutomation> createState() =>
|
|
||||||
_FetchRoutineScenesState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
|
|
||||||
with HelperResponsiveLayout {
|
with HelperResponsiveLayout {
|
||||||
@override
|
const FetchRoutineScenesAutomation({super.key});
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<RoutineBloc, RoutineState>(
|
return BlocBuilder<RoutineBloc, RoutineState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return state.isLoading
|
if (state.isLoading) return const Center(child: CircularProgressIndicator());
|
||||||
? const Center(
|
|
||||||
child: CircularProgressIndicator(),
|
return SingleChildScrollView(
|
||||||
)
|
|
||||||
: SingleChildScrollView(
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
_buildListTitle(context, "Scenes (Tab to Run)"),
|
||||||
"Scenes (Tab to Run)",
|
const SizedBox(height: 10),
|
||||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
Visibility(
|
||||||
color: ColorsManager.grayColor,
|
visible: state.scenes.isNotEmpty,
|
||||||
fontWeight: FontWeight.bold,
|
replacement: _buildEmptyState(context, "No scenes found"),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 200,
|
||||||
|
child: _buildScenes(state),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
if (state.scenes.isEmpty)
|
_buildListTitle(context, "Automations"),
|
||||||
Text(
|
const SizedBox(height: 3),
|
||||||
"No scenes found",
|
Visibility(
|
||||||
style: context.textTheme.bodyMedium?.copyWith(
|
visible: state.automations.isNotEmpty,
|
||||||
color: ColorsManager.grayColor,
|
replacement: _buildEmptyState(context, "No automations found"),
|
||||||
),
|
child: SizedBox(
|
||||||
),
|
|
||||||
if (state.scenes.isNotEmpty)
|
|
||||||
SizedBox(
|
|
||||||
height: 200,
|
height: 200,
|
||||||
child: ListView.builder(
|
child: _buildAutomations(state),
|
||||||
shrinkWrap: true,
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildAutomations(RoutineState state) {
|
||||||
|
return ListView.builder(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: state.automations.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final isLoading = state.automations.contains(state.automations[index].id);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
right: isSmallScreenSize(context) ? 4.0 : 8.0,
|
||||||
|
),
|
||||||
|
child: RoutineViewCard(
|
||||||
|
isLoading: isLoading,
|
||||||
|
onChanged: (v) {
|
||||||
|
context.read<RoutineBloc>().add(
|
||||||
|
UpdateAutomationStatus(
|
||||||
|
automationId: state.automations[index].id,
|
||||||
|
automationStatusUpdate: AutomationStatusUpdate(
|
||||||
|
spaceUuid: state.automations[index].spaceId,
|
||||||
|
isEnable: v,
|
||||||
|
),
|
||||||
|
communityId: state.automations[index].communityId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
status: state.automations[index].status,
|
||||||
|
communityId: '',
|
||||||
|
spaceId: state.automations[index].spaceId,
|
||||||
|
sceneId: '',
|
||||||
|
automationId: state.automations[index].id,
|
||||||
|
cardType: 'automations',
|
||||||
|
spaceName: state.automations[index].spaceName,
|
||||||
|
onTap: () {
|
||||||
|
BlocProvider.of<RoutineBloc>(context).add(
|
||||||
|
const CreateNewRoutineViewEvent(
|
||||||
|
createRoutineView: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
context.read<RoutineBloc>().add(
|
||||||
|
GetAutomationDetails(
|
||||||
|
automationId: state.automations[index].id,
|
||||||
|
isAutomation: true,
|
||||||
|
isUpdate: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
textString: state.automations[index].name,
|
||||||
|
icon: state.automations[index].icon ?? Assets.automation,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildScenes(RoutineState state) {
|
||||||
|
return ListView.builder(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
itemCount: state.scenes.length,
|
itemCount: state.scenes.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final scene = state.scenes[index];
|
final scene = state.scenes[index];
|
||||||
final isLoading =
|
final isLoading = state.loadingSceneId == scene.id;
|
||||||
state.loadingSceneId == scene.id;
|
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
right:
|
right: isSmallScreenSize(context) ? 4.0 : 8.0,
|
||||||
isSmallScreenSize(context) ? 4.0 : 8.0,
|
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
@ -78,141 +132,58 @@ class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
|
|||||||
context.read<RoutineBloc>().add(
|
context.read<RoutineBloc>().add(
|
||||||
SceneTrigger(
|
SceneTrigger(
|
||||||
sceneId: scene.id,
|
sceneId: scene.id,
|
||||||
name: scene.name));
|
name: scene.name,
|
||||||
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
status: state.scenes[index].status,
|
status: state.scenes[index].status,
|
||||||
communityId:
|
communityId: state.scenes[index].communityId,
|
||||||
state.scenes[index].communityId ??
|
|
||||||
'',
|
|
||||||
spaceId: state.scenes[index].spaceId,
|
spaceId: state.scenes[index].spaceId,
|
||||||
sceneId:
|
sceneId: state.scenes[index].sceneTuyaId!,
|
||||||
state.scenes[index].sceneTuyaId!,
|
|
||||||
automationId: state.scenes[index].id,
|
automationId: state.scenes[index].id,
|
||||||
cardType: 'scenes',
|
cardType: 'scenes',
|
||||||
spaceName:
|
spaceName: state.scenes[index].spaceName,
|
||||||
state.scenes[index].spaceName,
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
BlocProvider.of<RoutineBloc>(context)
|
BlocProvider.of<RoutineBloc>(context).add(
|
||||||
.add(
|
|
||||||
const CreateNewRoutineViewEvent(
|
const CreateNewRoutineViewEvent(
|
||||||
createRoutineView: true),
|
createRoutineView: true,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
context.read<RoutineBloc>().add(
|
context.read<RoutineBloc>().add(
|
||||||
GetSceneDetails(
|
GetSceneDetails(
|
||||||
sceneId:
|
sceneId: state.scenes[index].id,
|
||||||
state.scenes[index].id,
|
|
||||||
isTabToRun: true,
|
isTabToRun: true,
|
||||||
isUpdate: true,
|
isUpdate: true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
textString: state.scenes[index].name,
|
textString: state.scenes[index].name,
|
||||||
icon: state.scenes[index].icon ??
|
icon: state.scenes[index].icon ?? Assets.logoHorizontal,
|
||||||
Assets.logoHorizontal,
|
|
||||||
isFromScenes: true,
|
isFromScenes: true,
|
||||||
iconInBytes:
|
iconInBytes: state.scenes[index].iconInBytes,
|
||||||
state.scenes[index].iconInBytes,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
});
|
||||||
),
|
}
|
||||||
const SizedBox(height: 10),
|
|
||||||
Text(
|
Widget _buildListTitle(BuildContext context, String title) {
|
||||||
"Automations",
|
return Text(
|
||||||
|
title,
|
||||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||||
color: ColorsManager.grayColor,
|
color: ColorsManager.grayColor,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
const SizedBox(height: 3),
|
}
|
||||||
if (state.automations.isEmpty)
|
|
||||||
Text(
|
Widget _buildEmptyState(BuildContext context, String title) {
|
||||||
"No automations found",
|
return Text(
|
||||||
|
title,
|
||||||
style: context.textTheme.bodyMedium?.copyWith(
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
color: ColorsManager.grayColor,
|
color: ColorsManager.grayColor,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
if (state.automations.isNotEmpty)
|
|
||||||
SizedBox(
|
|
||||||
height: 200,
|
|
||||||
|
|
||||||
child: ListView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
itemCount: state.automations.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final isLoading = state.automations!
|
|
||||||
.contains(state.automations[index].id);
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
right: isSmallScreenSize(context)
|
|
||||||
? 4.0
|
|
||||||
: 8.0,
|
|
||||||
),
|
|
||||||
child: RoutineViewCard(
|
|
||||||
isLoading: isLoading,
|
|
||||||
onChanged: (v) {
|
|
||||||
context.read<RoutineBloc>().add(
|
|
||||||
UpdateAutomationStatus(
|
|
||||||
automationId: state
|
|
||||||
.automations[index].id,
|
|
||||||
automationStatusUpdate:
|
|
||||||
AutomationStatusUpdate(
|
|
||||||
spaceUuid: state
|
|
||||||
.automations[
|
|
||||||
index]
|
|
||||||
.spaceId,
|
|
||||||
isEnable: v),
|
|
||||||
communityId: state
|
|
||||||
.automations[index]
|
|
||||||
.communityId,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
status: state.automations[index].status,
|
|
||||||
communityId: '',
|
|
||||||
spaceId:
|
|
||||||
state.automations[index].spaceId,
|
|
||||||
sceneId: '',
|
|
||||||
automationId:
|
|
||||||
state.automations[index].id,
|
|
||||||
cardType: 'automations',
|
|
||||||
spaceName:
|
|
||||||
state.automations[index].spaceName,
|
|
||||||
onTap: () {
|
|
||||||
BlocProvider.of<RoutineBloc>(context)
|
|
||||||
.add(
|
|
||||||
const CreateNewRoutineViewEvent(
|
|
||||||
createRoutineView: true),
|
|
||||||
);
|
|
||||||
context.read<RoutineBloc>().add(
|
|
||||||
GetAutomationDetails(
|
|
||||||
automationId: state
|
|
||||||
.automations[index].id,
|
|
||||||
isAutomation: true,
|
|
||||||
isUpdate: true),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
textString:
|
|
||||||
state.automations[index].name,
|
|
||||||
icon: state.automations[index].icon ??
|
|
||||||
Assets.automation,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@ -66,7 +67,6 @@ class _RoutineViewCardState extends State<RoutineViewCard> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// Use widget.<mixinMethod> instead of just <mixinMethod>
|
|
||||||
final double cardWidth = widget.isSmallScreenSize(context)
|
final double cardWidth = widget.isSmallScreenSize(context)
|
||||||
? 120
|
? 120
|
||||||
: widget.isMediumScreenSize(context)
|
: widget.isMediumScreenSize(context)
|
||||||
@ -121,29 +121,29 @@ class _RoutineViewCardState extends State<RoutineViewCard> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 16,
|
width: 16,
|
||||||
height: 16,
|
height: 16,
|
||||||
child:
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
CircularProgressIndicator(strokeWidth: 2),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
CupertinoSwitch(
|
CupertinoSwitch(
|
||||||
activeColor: ColorsManager.primaryColor,
|
activeTrackColor: ColorsManager.primaryColor,
|
||||||
value: widget.status == 'enable',
|
value: widget.status == 'enable',
|
||||||
onChanged: widget.onChanged,
|
onChanged: widget.onChanged,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: const SizedBox(),
|
: const SizedBox(),
|
||||||
InkWell(
|
Column(
|
||||||
onTap: widget.onTap,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
children: [
|
||||||
Center(
|
Center(
|
||||||
|
child: InkWell(
|
||||||
|
customBorder: const CircleBorder(),
|
||||||
|
onTap: widget.onTap,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: ColorsManager.graysColor,
|
color: ColorsManager.graysColor,
|
||||||
borderRadius: BorderRadius.circular(120),
|
shape: BoxShape.circle,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: ColorsManager.greyColor,
|
color: ColorsManager.greyColor,
|
||||||
width: 2.0,
|
width: 2.0,
|
||||||
@ -159,8 +159,7 @@ class _RoutineViewCardState extends State<RoutineViewCard> {
|
|||||||
height: iconSize,
|
height: iconSize,
|
||||||
width: iconSize,
|
width: iconSize,
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
errorBuilder:
|
errorBuilder: (context, error, stackTrace) =>
|
||||||
(context, error, stackTrace) =>
|
|
||||||
Image.asset(
|
Image.asset(
|
||||||
Assets.logo,
|
Assets.logo,
|
||||||
height: iconSize,
|
height: iconSize,
|
||||||
@ -191,6 +190,7 @@ class _RoutineViewCardState extends State<RoutineViewCard> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 3),
|
padding: const EdgeInsets.symmetric(horizontal: 3),
|
||||||
@ -200,11 +200,10 @@ class _RoutineViewCardState extends State<RoutineViewCard> {
|
|||||||
widget.textString,
|
widget.textString,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
maxLines: 2,
|
maxLines: 1,
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
fontSize:
|
fontSize: widget.isSmallScreenSize(context) ? 10 : 12,
|
||||||
widget.isSmallScreenSize(context) ? 10 : 12,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (widget.spaceName != '')
|
if (widget.spaceName != '')
|
||||||
@ -220,14 +219,11 @@ class _RoutineViewCardState extends State<RoutineViewCard> {
|
|||||||
widget.spaceName,
|
widget.spaceName,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
maxLines: 2,
|
maxLines: 1,
|
||||||
style:
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
context.textTheme.bodySmall?.copyWith(
|
|
||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
fontSize:
|
fontSize:
|
||||||
widget.isSmallScreenSize(context)
|
widget.isSmallScreenSize(context) ? 10 : 12,
|
||||||
? 10
|
|
||||||
: 12,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -237,7 +233,6 @@ class _RoutineViewCardState extends State<RoutineViewCard> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -51,12 +51,12 @@ class _RoutineDevicesState extends State<RoutineDevices> {
|
|||||||
'productType': device.productType,
|
'productType': device.productType,
|
||||||
'functions': device.functions,
|
'functions': device.functions,
|
||||||
'uniqueCustomId': '',
|
'uniqueCustomId': '',
|
||||||
|
'tag': device.deviceTags!.isNotEmpty ? device.deviceTags![0].name : '',
|
||||||
|
'subSpace': device.deviceSubSpace?.subspaceName ?? '',
|
||||||
};
|
};
|
||||||
|
|
||||||
if (state.searchText != null && state.searchText!.isNotEmpty) {
|
if (state.searchText != null && state.searchText!.isNotEmpty) {
|
||||||
return device.name!
|
return device.name!.toLowerCase().contains(state.searchText!.toLowerCase())
|
||||||
.toLowerCase()
|
|
||||||
.contains(state.searchText!.toLowerCase())
|
|
||||||
? DraggableCard(
|
? DraggableCard(
|
||||||
imagePath: deviceData['imagePath'] as String,
|
imagePath: deviceData['imagePath'] as String,
|
||||||
title: deviceData['title'] as String,
|
title: deviceData['title'] as String,
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
|
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/ac/ac_operational_value.dart';
|
import 'package:syncrow_web/pages/routines/models/ac/ac_operational_value.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
|
||||||
|
|
||||||
class ACHelper {
|
class ACHelper {
|
||||||
static Future<Map<String, dynamic>?> showACFunctionsDialog({
|
static Future<Map<String, dynamic>?> showACFunctionsDialog({
|
||||||
@ -74,13 +75,26 @@ class ACHelper {
|
|||||||
child: _buildFunctionsList(
|
child: _buildFunctionsList(
|
||||||
context: context,
|
context: context,
|
||||||
acFunctions: acFunctions,
|
acFunctions: acFunctions,
|
||||||
onFunctionSelected:
|
device: device,
|
||||||
(functionCode, operationName) => context
|
onFunctionSelected: (functionCode, operationName) {
|
||||||
.read<FunctionBloc>()
|
RoutineTapFunctionHelper.onTapFunction(
|
||||||
.add(SelectFunction(
|
context,
|
||||||
functionCode: functionCode,
|
functionCode: functionCode,
|
||||||
operationName: operationName,
|
functionOperationName: operationName,
|
||||||
)),
|
functionValueDescription:
|
||||||
|
selectedFunctionData.valueDescription,
|
||||||
|
deviceUuid: device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'temp_set',
|
||||||
|
'temp_current',
|
||||||
|
],
|
||||||
|
defaultValue: functionCode == 'temp_set'
|
||||||
|
? 200
|
||||||
|
: functionCode == 'temp_current'
|
||||||
|
? -100
|
||||||
|
: 0,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Value selector
|
// Value selector
|
||||||
@ -139,6 +153,7 @@ class ACHelper {
|
|||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
required List<ACFunction> acFunctions,
|
required List<ACFunction> acFunctions,
|
||||||
required Function(String, String) onFunctionSelected,
|
required Function(String, String) onFunctionSelected,
|
||||||
|
required AllDevicesModel? device,
|
||||||
}) {
|
}) {
|
||||||
return ListView.separated(
|
return ListView.separated(
|
||||||
shrinkWrap: false,
|
shrinkWrap: false,
|
||||||
@ -191,8 +206,9 @@ class ACHelper {
|
|||||||
required String operationName,
|
required String operationName,
|
||||||
bool? removeComparators,
|
bool? removeComparators,
|
||||||
}) {
|
}) {
|
||||||
|
final initialVal = selectedFunction == 'temp_set' ? 200 : -100;
|
||||||
if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') {
|
if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') {
|
||||||
final initialValue = selectedFunctionData?.value ?? 250;
|
final initialValue = selectedFunctionData?.value ?? initialVal;
|
||||||
return _buildTemperatureSelector(
|
return _buildTemperatureSelector(
|
||||||
context: context,
|
context: context,
|
||||||
initialValue: initialValue,
|
initialValue: initialValue,
|
||||||
@ -205,8 +221,7 @@ class ACHelper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final selectedFn =
|
final selectedFn = acFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||||
acFunctions.firstWhere((f) => f.code == selectedFunction);
|
|
||||||
final values = selectedFn.getOperationalValues();
|
final values = selectedFn.getOperationalValues();
|
||||||
|
|
||||||
return _buildOperationalValuesList(
|
return _buildOperationalValuesList(
|
||||||
@ -287,7 +302,9 @@ class ACHelper {
|
|||||||
functionCode: selectCode,
|
functionCode: selectCode,
|
||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
condition: conditions[index],
|
condition: conditions[index],
|
||||||
value: selectedFunctionData?.value,
|
value: selectedFunctionData?.value ?? selectCode == 'temp_set'
|
||||||
|
? 200
|
||||||
|
: -100,
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -302,8 +319,7 @@ class ACHelper {
|
|||||||
minHeight: 40.0,
|
minHeight: 40.0,
|
||||||
minWidth: 40.0,
|
minWidth: 40.0,
|
||||||
),
|
),
|
||||||
isSelected:
|
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||||
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
|
||||||
children: conditions.map((c) => Text(c)).toList(),
|
children: conditions.map((c) => Text(c)).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -317,6 +333,7 @@ class ACHelper {
|
|||||||
DeviceFunctionData? selectedFunctionData,
|
DeviceFunctionData? selectedFunctionData,
|
||||||
String selectCode,
|
String selectCode,
|
||||||
) {
|
) {
|
||||||
|
final initialVal = selectCode == 'temp_set' ? 200 : -100;
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -324,7 +341,7 @@ class ACHelper {
|
|||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'${(initialValue ?? 200) / 10}°C',
|
'${(initialValue ?? initialVal) / 10}°C',
|
||||||
style: context.textTheme.headlineMedium!.copyWith(
|
style: context.textTheme.headlineMedium!.copyWith(
|
||||||
color: ColorsManager.primaryColorWithOpacity,
|
color: ColorsManager.primaryColorWithOpacity,
|
||||||
),
|
),
|
||||||
@ -397,9 +414,7 @@ class ACHelper {
|
|||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
isSelected
|
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||||
? Icons.radio_button_checked
|
|
||||||
: Icons.radio_button_unchecked,
|
|
||||||
size: 24,
|
size: 24,
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.primaryColorWithOpacity
|
? ColorsManager.primaryColorWithOpacity
|
||||||
@ -415,8 +430,7 @@ class ACHelper {
|
|||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value.value,
|
value: value.value,
|
||||||
condition: selectedFunctionData?.condition,
|
condition: selectedFunctionData?.condition,
|
||||||
valueDescription:
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
selectedFunctionData?.valueDescription,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -11,6 +11,7 @@ import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_senso
|
|||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_slider_selector.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_slider_selector.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_value_selector.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_value_selector.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_functions_list.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_functions_list.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart';
|
||||||
|
|
||||||
class CeilingSensorDialog extends StatefulWidget {
|
class CeilingSensorDialog extends StatefulWidget {
|
||||||
const CeilingSensorDialog({
|
const CeilingSensorDialog({
|
||||||
@ -34,6 +35,7 @@ class CeilingSensorDialog extends StatefulWidget {
|
|||||||
|
|
||||||
class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
|
class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
|
||||||
late final List<CpsFunctions> _cpsFunctions;
|
late final List<CpsFunctions> _cpsFunctions;
|
||||||
|
late final String _dialogHeaderText;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -45,6 +47,9 @@ class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
|
|||||||
}
|
}
|
||||||
return function.type == 'IF' || function.type == 'BOTH';
|
return function.type == 'IF' || function.type == 'BOTH';
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
|
final isIfDialog = widget.dialogType == 'IF';
|
||||||
|
_dialogHeaderText = isIfDialog ? 'Conditions' : 'Functions';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -66,15 +71,18 @@ class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const DialogHeader('Presence Sensor Condition'),
|
DialogHeader('Presence Sensor $_dialogHeaderText'),
|
||||||
Expanded(child: _buildMainContent(context, state)),
|
Expanded(child: _buildMainContent(context, state)),
|
||||||
DialogFooter(
|
DialogFooter(
|
||||||
onCancel: () => Navigator.pop(context),
|
onCancel: () => Navigator.pop(context),
|
||||||
onConfirm: state.addedFunctions.isNotEmpty
|
onConfirm: state.addedFunctions.isNotEmpty
|
||||||
? () {
|
? () {
|
||||||
|
final functions = _updateValuesForAddedFunctions(
|
||||||
|
state.addedFunctions,
|
||||||
|
);
|
||||||
context.read<RoutineBloc>().add(
|
context.read<RoutineBloc>().add(
|
||||||
AddFunctionToRoutine(
|
AddFunctionToRoutine(
|
||||||
state.addedFunctions,
|
functions,
|
||||||
'${widget.uniqueCustomId}',
|
'${widget.uniqueCustomId}',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -123,7 +131,12 @@ class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
|
|||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
CpsFunctionsList(cpsFunctions: _cpsFunctions),
|
CpsFunctionsList(
|
||||||
|
cpsFunctions: _cpsFunctions,
|
||||||
|
device: widget.device,
|
||||||
|
selectedFunctionData: selectedFunctionData,
|
||||||
|
dialogType: widget.dialogType,
|
||||||
|
),
|
||||||
if (state.selectedFunction != null)
|
if (state.selectedFunction != null)
|
||||||
Expanded(
|
Expanded(
|
||||||
child: isToggleFunction
|
child: isToggleFunction
|
||||||
@ -148,4 +161,64 @@ class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const _mappableSteppedFunctions = <String>{
|
||||||
|
'static_max_dis',
|
||||||
|
'presence_reference',
|
||||||
|
'moving_reference',
|
||||||
|
'perceptual_boundary',
|
||||||
|
'moving_boundary',
|
||||||
|
'moving_rigger_time',
|
||||||
|
'moving_static_time',
|
||||||
|
'none_body_time',
|
||||||
|
'moving_max_dis',
|
||||||
|
'moving_range',
|
||||||
|
'presence_range',
|
||||||
|
};
|
||||||
|
|
||||||
|
List<DeviceFunctionData> _updateValuesForAddedFunctions(
|
||||||
|
List<DeviceFunctionData> addedFunctions,
|
||||||
|
) {
|
||||||
|
return addedFunctions.map((function) {
|
||||||
|
final shouldMapValue = _mappableSteppedFunctions.contains(
|
||||||
|
function.functionCode,
|
||||||
|
);
|
||||||
|
if (shouldMapValue) {
|
||||||
|
final mappedValue = _mapSteppedValue(
|
||||||
|
value: function.value,
|
||||||
|
inputStep: CpsSliderHelpers.dividendOfRange(function.functionCode),
|
||||||
|
inputRange: CpsSliderHelpers.sliderRange(function.functionCode),
|
||||||
|
outputRange: CpsSliderHelpers.mappedRange(function.functionCode),
|
||||||
|
);
|
||||||
|
return DeviceFunctionData(
|
||||||
|
value: mappedValue,
|
||||||
|
entityId: function.entityId,
|
||||||
|
functionCode: function.functionCode,
|
||||||
|
operationName: function.operationName,
|
||||||
|
condition: function.condition,
|
||||||
|
actionExecutor: function.actionExecutor,
|
||||||
|
valueDescription: function.valueDescription,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return function;
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
int _mapSteppedValue({
|
||||||
|
required (double min, double max) inputRange,
|
||||||
|
required double inputStep,
|
||||||
|
required (double min, double max, double dividend) outputRange,
|
||||||
|
required double value,
|
||||||
|
}) {
|
||||||
|
final (inputMin, inputMax) = inputRange;
|
||||||
|
final (outputMin, outputMax, outputStep) = outputRange;
|
||||||
|
|
||||||
|
final clampedValue = value.clamp(inputMin, inputMax);
|
||||||
|
|
||||||
|
final stepsFromMin = ((clampedValue - inputMin) / inputStep).round();
|
||||||
|
|
||||||
|
final mappedValue = outputMin + (stepsFromMin * outputStep);
|
||||||
|
|
||||||
|
return mappedValue.clamp(outputMin, outputMax).round();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
|
|||||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
||||||
|
|
||||||
class CpsDialogSliderSelector extends StatelessWidget {
|
class CpsDialogSliderSelector extends StatelessWidget {
|
||||||
@ -31,10 +32,13 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
|||||||
return SliderValueSelector(
|
return SliderValueSelector(
|
||||||
currentCondition: selectedFunctionData.condition,
|
currentCondition: selectedFunctionData.condition,
|
||||||
dialogType: dialogType,
|
dialogType: dialogType,
|
||||||
sliderRange: _sliderRange,
|
sliderRange: CpsSliderHelpers.sliderRange(selectedFunctionData.functionCode),
|
||||||
displayedValue: _displayText,
|
displayedValue: CpsSliderHelpers.displayText(
|
||||||
|
value: selectedFunctionData.value,
|
||||||
|
functionCode: selectedFunctionData.functionCode,
|
||||||
|
),
|
||||||
initialValue: selectedFunctionData.value ?? 0,
|
initialValue: selectedFunctionData.value ?? 0,
|
||||||
unit: _unit,
|
unit: CpsSliderHelpers.unit(selectedFunctionData.functionCode),
|
||||||
onConditionChanged: (condition) => context.read<FunctionBloc>().add(
|
onConditionChanged: (condition) => context.read<FunctionBloc>().add(
|
||||||
AddFunction(
|
AddFunction(
|
||||||
functionData: DeviceFunctionData(
|
functionData: DeviceFunctionData(
|
||||||
@ -42,7 +46,7 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
|||||||
functionCode: selectedFunction,
|
functionCode: selectedFunction,
|
||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
condition: condition,
|
condition: condition,
|
||||||
value: selectedFunctionData.value,
|
value: selectedFunctionData.value ?? 0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -52,85 +56,14 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
|||||||
entityId: device?.uuid ?? '',
|
entityId: device?.uuid ?? '',
|
||||||
functionCode: selectedFunction,
|
functionCode: selectedFunction,
|
||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value,
|
value: double.parse(value.toStringAsFixed(2)),
|
||||||
condition: selectedFunctionData.condition,
|
condition: selectedFunctionData.condition,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
dividendOfRange: _dividendOfRange,
|
dividendOfRange: CpsSliderHelpers.dividendOfRange(
|
||||||
|
selectedFunctionData.functionCode,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
(double, double) get _sliderRange => switch (selectedFunctionData.functionCode) {
|
|
||||||
'moving_speed' => (0, 32),
|
|
||||||
'sensitivity' => (0, 10),
|
|
||||||
'space_static_val' => (0, 255),
|
|
||||||
'space_move_val' => (0, 255),
|
|
||||||
'moving_max_dis' => (0, 10),
|
|
||||||
'static_max_dis' => (0, 10),
|
|
||||||
'moving_range' => (0, 25.5),
|
|
||||||
'presence_range' => (0, 25.5),
|
|
||||||
'presence_judgement_threshold' => (0, 255),
|
|
||||||
'motion_amplitude_trigger_threshold' => (0, 255),
|
|
||||||
'perceptual_boundary' => (0, 5),
|
|
||||||
'moving_boundary' => (0, 5),
|
|
||||||
'moving_rigger_time' => (0, 2),
|
|
||||||
'moving_static_time' => (0, 50),
|
|
||||||
'none_body_time' => (0, 300),
|
|
||||||
_ => (0, 300),
|
|
||||||
};
|
|
||||||
|
|
||||||
String get _displayText {
|
|
||||||
final value = selectedFunctionData.value;
|
|
||||||
final parsedValue = double.tryParse('$value');
|
|
||||||
|
|
||||||
return switch (selectedFunctionData.functionCode) {
|
|
||||||
'moving_max_dis' ||
|
|
||||||
'static_max_dis' ||
|
|
||||||
'moving_range' ||
|
|
||||||
'presence_range' ||
|
|
||||||
'perceptual_boundary' ||
|
|
||||||
'moving_boundary' =>
|
|
||||||
parsedValue?.toStringAsFixed(1) ?? '0',
|
|
||||||
'moving_rigger_time' => parsedValue?.toStringAsFixed(2) ?? '0',
|
|
||||||
'moving_static_time' ||
|
|
||||||
'none_body_time' =>
|
|
||||||
parsedValue?.toStringAsFixed(2) ?? '0',
|
|
||||||
_ => '${parsedValue?.toStringAsFixed(0) ?? 0}',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
String get _unit {
|
|
||||||
return switch (selectedFunctionData.functionCode) {
|
|
||||||
'moving_max_dis' ||
|
|
||||||
'static_max_dis' ||
|
|
||||||
'moving_range' ||
|
|
||||||
'presence_range' ||
|
|
||||||
'perceptual_boundary' ||
|
|
||||||
'moving_boundary' =>
|
|
||||||
'M',
|
|
||||||
'moving_rigger_time' || 'moving_static_time' || 'none_body_time' => 'sec',
|
|
||||||
_ => '',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
double get _dividendOfRange => switch (selectedFunctionData.functionCode) {
|
|
||||||
'sensitivity' => 1,
|
|
||||||
'moving_speed' => 1,
|
|
||||||
'space_static_val' => 1,
|
|
||||||
'space_move_val' => 1,
|
|
||||||
'presence_reference' => 5,
|
|
||||||
'moving_reference' => 5,
|
|
||||||
'moving_max_dis' => 0.5,
|
|
||||||
'static_max_dis' => 0.5,
|
|
||||||
'moving_range' => 0.1,
|
|
||||||
'presence_range' => 0.1,
|
|
||||||
'perceptual_boundary' => 0.5,
|
|
||||||
'moving_boundary' => 0.5,
|
|
||||||
'moving_rigger_time' => 0.1,
|
|
||||||
'moving_static_time' => 1.0,
|
|
||||||
'none_body_time' => 5.0,
|
|
||||||
'sports_para' => 1.0,
|
|
||||||
_ => 1,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,24 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialog_function_list_tile.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialog_function_list_tile.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class CpsFunctionsList extends StatelessWidget {
|
class CpsFunctionsList extends StatelessWidget {
|
||||||
const CpsFunctionsList({required this.cpsFunctions, super.key});
|
const CpsFunctionsList({
|
||||||
|
required this.cpsFunctions,
|
||||||
|
required this.device,
|
||||||
|
required this.selectedFunctionData,
|
||||||
|
required this.dialogType,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
final List<CpsFunctions> cpsFunctions;
|
final List<CpsFunctions> cpsFunctions;
|
||||||
|
final AllDevicesModel? device;
|
||||||
|
final DeviceFunctionData? selectedFunctionData;
|
||||||
|
final String dialogType;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -26,11 +36,26 @@ class CpsFunctionsList extends StatelessWidget {
|
|||||||
return RoutineDialogFunctionListTile(
|
return RoutineDialogFunctionListTile(
|
||||||
iconPath: function.icon,
|
iconPath: function.icon,
|
||||||
operationName: function.operationName,
|
operationName: function.operationName,
|
||||||
onTap: () => context.read<FunctionBloc>().add(
|
onTap: () => RoutineTapFunctionHelper.onTapFunction(
|
||||||
SelectFunction(
|
context,
|
||||||
functionCode: function.code,
|
functionCode: function.code,
|
||||||
operationName: function.operationName,
|
functionOperationName: function.operationName,
|
||||||
),
|
functionValueDescription: selectedFunctionData?.valueDescription,
|
||||||
|
deviceUuid: device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'static_max_dis',
|
||||||
|
'presence_reference',
|
||||||
|
'moving_reference',
|
||||||
|
'perceptual_boundary',
|
||||||
|
'moving_boundary',
|
||||||
|
'moving_rigger_time',
|
||||||
|
'moving_static_time',
|
||||||
|
'none_body_time',
|
||||||
|
'moving_max_dis',
|
||||||
|
'moving_range',
|
||||||
|
'presence_range',
|
||||||
|
if (dialogType == "IF") 'sensitivity',
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -0,0 +1,90 @@
|
|||||||
|
abstract final class CpsSliderHelpers {
|
||||||
|
static (double min, double max, double step) mappedRange(String functionCode) {
|
||||||
|
final (defaultMin, defaultMax) = sliderRange(functionCode);
|
||||||
|
final defaultDivdidend = dividendOfRange(functionCode);
|
||||||
|
return switch (functionCode) {
|
||||||
|
'static_max_dis' => (0, 500, 50),
|
||||||
|
'presence_reference' => (0, 255, 5),
|
||||||
|
'moving_reference' => (0, 255, 5),
|
||||||
|
'perceptual_boundary' => (0, 500, 50),
|
||||||
|
'moving_boundary' => (0, 500, 50),
|
||||||
|
'moving_rigger_time' => (0, 2000, 100),
|
||||||
|
'moving_static_time' => (0, 60000, 1000),
|
||||||
|
'none_body_time' => (0, 300000, 5000),
|
||||||
|
'moving_max_dis' => (0, 500, 50),
|
||||||
|
'moving_range' => (0, 255, 5),
|
||||||
|
'presence_range' => (0, 255, 5),
|
||||||
|
_ => (defaultMin, defaultMax, defaultDivdidend),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static (double min, double max) sliderRange(String functionCode) =>
|
||||||
|
switch (functionCode) {
|
||||||
|
'moving_speed' => (0, 32),
|
||||||
|
'sensitivity' => (0, 10),
|
||||||
|
'space_static_val' => (0, 255),
|
||||||
|
'space_move_val' => (0, 255),
|
||||||
|
'moving_max_dis' => (0, 10),
|
||||||
|
'static_max_dis' => (0, 10),
|
||||||
|
'moving_range' => (0, 25.5),
|
||||||
|
'presence_range' => (0, 25.5),
|
||||||
|
'presence_judgement_threshold' => (0, 255),
|
||||||
|
'motion_amplitude_trigger_threshold' => (0, 255),
|
||||||
|
'perceptual_boundary' => (0, 5),
|
||||||
|
'moving_boundary' => (0, 5),
|
||||||
|
'moving_rigger_time' => (0, 2),
|
||||||
|
'moving_static_time' => (0, 50),
|
||||||
|
'none_body_time' => (0, 300),
|
||||||
|
'sports_para' => (0, 100),
|
||||||
|
_ => (0, 300),
|
||||||
|
};
|
||||||
|
|
||||||
|
static double dividendOfRange(String functionCode) => switch (functionCode) {
|
||||||
|
'presence_reference' => 5,
|
||||||
|
'moving_reference' => 5,
|
||||||
|
'moving_max_dis' => 0.5,
|
||||||
|
'static_max_dis' => 0.5,
|
||||||
|
'moving_range' => 0.1,
|
||||||
|
'presence_range' => 0.1,
|
||||||
|
'perceptual_boundary' => 0.5,
|
||||||
|
'moving_boundary' => 0.5,
|
||||||
|
'moving_rigger_time' => 0.1,
|
||||||
|
'moving_static_time' => 1.0,
|
||||||
|
'none_body_time' => 5.0,
|
||||||
|
_ => 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static String unit(String functionCode) => switch (functionCode) {
|
||||||
|
'moving_max_dis' ||
|
||||||
|
'static_max_dis' ||
|
||||||
|
'moving_range' ||
|
||||||
|
'presence_range' ||
|
||||||
|
'perceptual_boundary' ||
|
||||||
|
'moving_boundary' =>
|
||||||
|
'M',
|
||||||
|
'moving_rigger_time' || 'moving_static_time' || 'none_body_time' => 'sec',
|
||||||
|
_ => '',
|
||||||
|
};
|
||||||
|
|
||||||
|
static String displayText({
|
||||||
|
required dynamic value,
|
||||||
|
required String functionCode,
|
||||||
|
}) {
|
||||||
|
final parsedValue = double.tryParse('$value');
|
||||||
|
|
||||||
|
return switch (functionCode) {
|
||||||
|
'moving_max_dis' ||
|
||||||
|
'static_max_dis' ||
|
||||||
|
'moving_range' ||
|
||||||
|
'presence_range' ||
|
||||||
|
'perceptual_boundary' ||
|
||||||
|
'moving_boundary' =>
|
||||||
|
parsedValue?.toStringAsFixed(1) ?? '0',
|
||||||
|
'moving_rigger_time' => parsedValue?.toStringAsFixed(2) ?? '0',
|
||||||
|
'moving_static_time' ||
|
||||||
|
'none_body_time' =>
|
||||||
|
parsedValue?.toStringAsFixed(2) ?? '0',
|
||||||
|
_ => '${parsedValue?.toStringAsFixed(0) ?? 0}',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
|
|
||||||
|
abstract final class RoutineTapFunctionHelper {
|
||||||
|
const RoutineTapFunctionHelper._();
|
||||||
|
|
||||||
|
static void onTapFunction(
|
||||||
|
BuildContext context, {
|
||||||
|
required String functionCode,
|
||||||
|
required String functionOperationName,
|
||||||
|
required String? functionValueDescription,
|
||||||
|
required String? deviceUuid,
|
||||||
|
required List<String> codesToAddIntoFunctionsWithDefaultValue,
|
||||||
|
int defaultValue = 0,
|
||||||
|
}) {
|
||||||
|
final functionsBloc = context.read<FunctionBloc>();
|
||||||
|
functionsBloc.add(
|
||||||
|
SelectFunction(
|
||||||
|
functionCode: functionCode,
|
||||||
|
operationName: functionOperationName,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final addedFunctions = functionsBloc.state.addedFunctions;
|
||||||
|
final isFunctionAlreadyAdded = addedFunctions.any(
|
||||||
|
(e) => e.functionCode == functionCode,
|
||||||
|
);
|
||||||
|
final shouldAddFunction =
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue.contains(functionCode);
|
||||||
|
if (!isFunctionAlreadyAdded && shouldAddFunction) {
|
||||||
|
context.read<FunctionBloc>().add(
|
||||||
|
AddFunction(
|
||||||
|
functionData: DeviceFunctionData(
|
||||||
|
entityId: deviceUuid ?? '',
|
||||||
|
functionCode: functionCode,
|
||||||
|
operationName: functionOperationName,
|
||||||
|
value: defaultValue,
|
||||||
|
condition: '==',
|
||||||
|
valueDescription: functionValueDescription,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,13 +5,13 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
|
|||||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
|
import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
|
|
||||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/one_gang_switch.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/one_gang_switch.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
@ -87,15 +87,19 @@ class OneGangSwitchHelper {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () =>
|
||||||
context
|
RoutineTapFunctionHelper.onTapFunction(
|
||||||
.read<FunctionBloc>()
|
context,
|
||||||
.add(SelectFunction(
|
|
||||||
functionCode: function.code,
|
functionCode: function.code,
|
||||||
operationName:
|
functionOperationName:
|
||||||
function.operationName,
|
function.operationName,
|
||||||
));
|
functionValueDescription:
|
||||||
},
|
selectedFunctionData.valueDescription,
|
||||||
|
deviceUuid: device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'countdown_1',
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -170,7 +174,7 @@ class OneGangSwitchHelper {
|
|||||||
required bool removeComparetors,
|
required bool removeComparetors,
|
||||||
}) {
|
}) {
|
||||||
if (selectedFunction == 'countdown_1') {
|
if (selectedFunction == 'countdown_1') {
|
||||||
final initialValue = selectedFunctionData?.value ?? 200;
|
final initialValue = selectedFunctionData?.value ?? 0;
|
||||||
return _buildCountDownSelector(
|
return _buildCountDownSelector(
|
||||||
context: context,
|
context: context,
|
||||||
initialValue: initialValue,
|
initialValue: initialValue,
|
||||||
@ -256,7 +260,7 @@ class OneGangSwitchHelper {
|
|||||||
functionCode: selectCode,
|
functionCode: selectCode,
|
||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
condition: conditions[index],
|
condition: conditions[index],
|
||||||
value: selectedFunctionData?.value,
|
value: selectedFunctionData?.value ?? 0,
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -271,8 +275,7 @@ class OneGangSwitchHelper {
|
|||||||
minHeight: 40.0,
|
minHeight: 40.0,
|
||||||
minWidth: 40.0,
|
minWidth: 40.0,
|
||||||
),
|
),
|
||||||
isSelected:
|
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||||
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
|
||||||
children: conditions.map((c) => Text(c)).toList(),
|
children: conditions.map((c) => Text(c)).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -308,20 +311,21 @@ class OneGangSwitchHelper {
|
|||||||
DeviceFunctionData? selectedFunctionData,
|
DeviceFunctionData? selectedFunctionData,
|
||||||
String selectCode,
|
String selectCode,
|
||||||
) {
|
) {
|
||||||
|
const twelveHoursInSeconds = 43200.0;
|
||||||
final operationalValues = SwitchOperationalValue(
|
final operationalValues = SwitchOperationalValue(
|
||||||
icon: '',
|
icon: '',
|
||||||
description: "sec",
|
description: "sec",
|
||||||
value: 0.0,
|
value: 0.0,
|
||||||
minValue: 0,
|
minValue: 0,
|
||||||
maxValue: 86400,
|
maxValue: twelveHoursInSeconds,
|
||||||
stepValue: 1,
|
stepValue: 1,
|
||||||
);
|
);
|
||||||
return Slider(
|
return Slider(
|
||||||
value: (initialValue ?? 0).toDouble(),
|
value: (initialValue ?? 0).toDouble(),
|
||||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||||
divisions: (((operationalValues.maxValue ?? 0) -
|
divisions:
|
||||||
(operationalValues.minValue ?? 0)) /
|
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
||||||
(operationalValues.stepValue ?? 1))
|
(operationalValues.stepValue ?? 1))
|
||||||
.round(),
|
.round(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -373,9 +377,7 @@ class OneGangSwitchHelper {
|
|||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
isSelected
|
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||||
? Icons.radio_button_checked
|
|
||||||
: Icons.radio_button_unchecked,
|
|
||||||
size: 24,
|
size: 24,
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.primaryColorWithOpacity
|
? ColorsManager.primaryColorWithOpacity
|
||||||
@ -391,8 +393,7 @@ class OneGangSwitchHelper {
|
|||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value.value,
|
value: value.value,
|
||||||
condition: selectedFunctionData?.condition,
|
condition: selectedFunctionData?.condition,
|
||||||
valueDescription:
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
selectedFunctionData?.valueDescription,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_func
|
|||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
@ -85,15 +86,21 @@ class ThreeGangSwitchHelper {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () =>
|
||||||
context
|
RoutineTapFunctionHelper.onTapFunction(
|
||||||
.read<FunctionBloc>()
|
context,
|
||||||
.add(SelectFunction(
|
|
||||||
functionCode: function.code,
|
functionCode: function.code,
|
||||||
operationName:
|
functionOperationName:
|
||||||
function.operationName,
|
function.operationName,
|
||||||
));
|
functionValueDescription:
|
||||||
},
|
selectedFunctionData.valueDescription,
|
||||||
|
deviceUuid: device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'countdown_1',
|
||||||
|
'countdown_2',
|
||||||
|
'countdown_3',
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -170,7 +177,7 @@ class ThreeGangSwitchHelper {
|
|||||||
if (selectedFunction == 'countdown_1' ||
|
if (selectedFunction == 'countdown_1' ||
|
||||||
selectedFunction == 'countdown_2' ||
|
selectedFunction == 'countdown_2' ||
|
||||||
selectedFunction == 'countdown_3') {
|
selectedFunction == 'countdown_3') {
|
||||||
final initialValue = selectedFunctionData?.value ?? 200;
|
final initialValue = selectedFunctionData?.value ?? 0;
|
||||||
return _buildTemperatureSelector(
|
return _buildTemperatureSelector(
|
||||||
context: context,
|
context: context,
|
||||||
initialValue: initialValue,
|
initialValue: initialValue,
|
||||||
@ -183,8 +190,7 @@ class ThreeGangSwitchHelper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final selectedFn =
|
final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||||
switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
|
||||||
final values = selectedFn.getOperationalValues();
|
final values = selectedFn.getOperationalValues();
|
||||||
|
|
||||||
return _buildOperationalValuesList(
|
return _buildOperationalValuesList(
|
||||||
@ -251,7 +257,7 @@ class ThreeGangSwitchHelper {
|
|||||||
functionCode: selectCode,
|
functionCode: selectCode,
|
||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
condition: conditions[index],
|
condition: conditions[index],
|
||||||
value: selectedFunctionData?.value,
|
value: selectedFunctionData?.value ?? 0,
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -266,8 +272,7 @@ class ThreeGangSwitchHelper {
|
|||||||
minHeight: 40.0,
|
minHeight: 40.0,
|
||||||
minWidth: 40.0,
|
minWidth: 40.0,
|
||||||
),
|
),
|
||||||
isSelected:
|
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||||
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
|
||||||
children: conditions.map((c) => Text(c)).toList(),
|
children: conditions.map((c) => Text(c)).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -303,20 +308,21 @@ class ThreeGangSwitchHelper {
|
|||||||
DeviceFunctionData? selectedFunctionData,
|
DeviceFunctionData? selectedFunctionData,
|
||||||
String selectCode,
|
String selectCode,
|
||||||
) {
|
) {
|
||||||
|
const twelveHoursInSeconds = 43200.0;
|
||||||
final operationalValues = SwitchOperationalValue(
|
final operationalValues = SwitchOperationalValue(
|
||||||
icon: '',
|
icon: '',
|
||||||
description: "sec",
|
description: "sec",
|
||||||
value: 0.0,
|
value: 0.0,
|
||||||
minValue: 0,
|
minValue: 0,
|
||||||
maxValue: 86400,
|
maxValue: twelveHoursInSeconds,
|
||||||
stepValue: 1,
|
stepValue: 1,
|
||||||
);
|
);
|
||||||
return Slider(
|
return Slider(
|
||||||
value: (initialValue ?? 0).toDouble(),
|
value: (initialValue ?? 0).toDouble(),
|
||||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||||
divisions: (((operationalValues.maxValue ?? 0) -
|
divisions:
|
||||||
(operationalValues.minValue ?? 0)) /
|
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
||||||
(operationalValues.stepValue ?? 1))
|
(operationalValues.stepValue ?? 1))
|
||||||
.round(),
|
.round(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -368,9 +374,7 @@ class ThreeGangSwitchHelper {
|
|||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
isSelected
|
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||||
? Icons.radio_button_checked
|
|
||||||
: Icons.radio_button_unchecked,
|
|
||||||
size: 24,
|
size: 24,
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.primaryColorWithOpacity
|
? ColorsManager.primaryColorWithOpacity
|
||||||
@ -386,8 +390,7 @@ class ThreeGangSwitchHelper {
|
|||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value.value,
|
value: value.value,
|
||||||
condition: selectedFunctionData?.condition,
|
condition: selectedFunctionData?.condition,
|
||||||
valueDescription:
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
selectedFunctionData?.valueDescription,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_func
|
|||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
@ -85,15 +86,20 @@ class TwoGangSwitchHelper {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () =>
|
||||||
context
|
RoutineTapFunctionHelper.onTapFunction(
|
||||||
.read<FunctionBloc>()
|
context,
|
||||||
.add(SelectFunction(
|
|
||||||
functionCode: function.code,
|
functionCode: function.code,
|
||||||
operationName:
|
functionOperationName:
|
||||||
function.operationName,
|
function.operationName,
|
||||||
));
|
functionValueDescription:
|
||||||
},
|
selectedFunctionData.valueDescription,
|
||||||
|
deviceUuid: device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'countdown_1',
|
||||||
|
'countdown_2',
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -167,9 +173,8 @@ class TwoGangSwitchHelper {
|
|||||||
required String operationName,
|
required String operationName,
|
||||||
required bool removeComparetors,
|
required bool removeComparetors,
|
||||||
}) {
|
}) {
|
||||||
if (selectedFunction == 'countdown_1' ||
|
if (selectedFunction == 'countdown_1' || selectedFunction == 'countdown_2') {
|
||||||
selectedFunction == 'countdown_2') {
|
final initialValue = selectedFunctionData?.value ?? 0;
|
||||||
final initialValue = selectedFunctionData?.value ?? 200;
|
|
||||||
return _buildTemperatureSelector(
|
return _buildTemperatureSelector(
|
||||||
context: context,
|
context: context,
|
||||||
initialValue: initialValue,
|
initialValue: initialValue,
|
||||||
@ -182,8 +187,7 @@ class TwoGangSwitchHelper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final selectedFn =
|
final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||||
switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
|
||||||
final values = selectedFn.getOperationalValues();
|
final values = selectedFn.getOperationalValues();
|
||||||
|
|
||||||
return _buildOperationalValuesList(
|
return _buildOperationalValuesList(
|
||||||
@ -250,7 +254,7 @@ class TwoGangSwitchHelper {
|
|||||||
functionCode: selectCode,
|
functionCode: selectCode,
|
||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
condition: conditions[index],
|
condition: conditions[index],
|
||||||
value: selectedFunctionData?.value,
|
value: selectedFunctionData?.value ?? 0,
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -265,8 +269,7 @@ class TwoGangSwitchHelper {
|
|||||||
minHeight: 40.0,
|
minHeight: 40.0,
|
||||||
minWidth: 40.0,
|
minWidth: 40.0,
|
||||||
),
|
),
|
||||||
isSelected:
|
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||||
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
|
||||||
children: conditions.map((c) => Text(c)).toList(),
|
children: conditions.map((c) => Text(c)).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -302,20 +305,21 @@ class TwoGangSwitchHelper {
|
|||||||
DeviceFunctionData? selectedFunctionData,
|
DeviceFunctionData? selectedFunctionData,
|
||||||
String selectCode,
|
String selectCode,
|
||||||
) {
|
) {
|
||||||
|
const twelveHoursInSeconds = 43200.0;
|
||||||
final operationalValues = SwitchOperationalValue(
|
final operationalValues = SwitchOperationalValue(
|
||||||
icon: '',
|
icon: '',
|
||||||
description: "sec",
|
description: "sec",
|
||||||
value: 0.0,
|
value: 0.0,
|
||||||
minValue: 0,
|
minValue: 0,
|
||||||
maxValue: 86400,
|
maxValue: twelveHoursInSeconds,
|
||||||
stepValue: 1,
|
stepValue: 1,
|
||||||
);
|
);
|
||||||
return Slider(
|
return Slider(
|
||||||
value: (initialValue ?? 0).toDouble(),
|
value: (initialValue ?? 0).toDouble(),
|
||||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||||
divisions: (((operationalValues.maxValue ?? 0) -
|
divisions:
|
||||||
(operationalValues.minValue ?? 0)) /
|
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
||||||
(operationalValues.stepValue ?? 1))
|
(operationalValues.stepValue ?? 1))
|
||||||
.round(),
|
.round(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -367,9 +371,7 @@ class TwoGangSwitchHelper {
|
|||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
isSelected
|
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||||
? Icons.radio_button_checked
|
|
||||||
: Icons.radio_button_unchecked,
|
|
||||||
size: 24,
|
size: 24,
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.primaryColorWithOpacity
|
? ColorsManager.primaryColorWithOpacity
|
||||||
@ -385,8 +387,7 @@ class TwoGangSwitchHelper {
|
|||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value.value,
|
value: value.value,
|
||||||
condition: selectedFunctionData?.condition,
|
condition: selectedFunctionData?.condition,
|
||||||
valueDescription:
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
selectedFunctionData?.valueDescription,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
@ -28,9 +27,12 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_hoursController = FixedExtentScrollController(initialItem: widget.initialHours);
|
_hoursController =
|
||||||
_minutesController = FixedExtentScrollController(initialItem: widget.initialMinutes);
|
FixedExtentScrollController(initialItem: widget.initialHours);
|
||||||
_secondsController = FixedExtentScrollController(initialItem: widget.initialSeconds);
|
_minutesController =
|
||||||
|
FixedExtentScrollController(initialItem: widget.initialMinutes);
|
||||||
|
_secondsController =
|
||||||
|
FixedExtentScrollController(initialItem: widget.initialSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -47,6 +49,8 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_hoursController.dispose();
|
_hoursController.dispose();
|
||||||
@ -63,24 +67,26 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
|
|||||||
_buildPickerColumn(
|
_buildPickerColumn(
|
||||||
label: 'h',
|
label: 'h',
|
||||||
controller: _hoursController,
|
controller: _hoursController,
|
||||||
itemCount: 24,
|
itemCount: 3,
|
||||||
onChanged: (value) => _handleTimeChange(
|
onChanged: (value) {
|
||||||
|
_handleTimeChange(
|
||||||
value,
|
value,
|
||||||
_minutesController.selectedItem,
|
_minutesController.selectedItem,
|
||||||
_secondsController.selectedItem,
|
_secondsController.selectedItem,
|
||||||
),
|
);
|
||||||
),
|
}),
|
||||||
const SizedBox(width: 5),
|
const SizedBox(width: 5),
|
||||||
_buildPickerColumn(
|
_buildPickerColumn(
|
||||||
label: 'm',
|
label: 'm',
|
||||||
controller: _minutesController,
|
controller: _minutesController,
|
||||||
itemCount: 60,
|
itemCount: 60,
|
||||||
onChanged: (value) => _handleTimeChange(
|
onChanged: (value) {
|
||||||
|
_handleTimeChange(
|
||||||
_hoursController.selectedItem,
|
_hoursController.selectedItem,
|
||||||
value,
|
value,
|
||||||
_secondsController.selectedItem,
|
_secondsController.selectedItem,
|
||||||
),
|
);
|
||||||
),
|
}),
|
||||||
const SizedBox(width: 5),
|
const SizedBox(width: 5),
|
||||||
_buildPickerColumn(
|
_buildPickerColumn(
|
||||||
label: 's',
|
label: 's',
|
||||||
@ -97,6 +103,19 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _handleTimeChange(int hours, int minutes, int seconds) {
|
void _handleTimeChange(int hours, int minutes, int seconds) {
|
||||||
|
int total = hours * 3600 + minutes * 60 + seconds;
|
||||||
|
if (total > 10000) {
|
||||||
|
hours = 2;
|
||||||
|
minutes = 46;
|
||||||
|
seconds = 40;
|
||||||
|
total = 10000;
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_hoursController.jumpToItem(hours);
|
||||||
|
_minutesController.jumpToItem(minutes);
|
||||||
|
_secondsController.jumpToItem(seconds);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
widget.onTimeChanged(hours, minutes, seconds);
|
widget.onTimeChanged(hours, minutes, seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
|||||||
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_value_selector_widget.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_value_selector_widget.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
@ -111,13 +112,23 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
|
|||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
_buildFunctionList(context),
|
_buildFunctionList(context, state),
|
||||||
if (state.selectedFunction != null) _buildValueSelector(context, state),
|
if (state.selectedFunction != null) _buildValueSelector(context, state),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildFunctionList(BuildContext context) {
|
Widget _buildFunctionList(BuildContext context, FunctionBlocState state) {
|
||||||
|
final selectedFunction = state.selectedFunction;
|
||||||
|
final selectedFunctionData = state.addedFunctions.firstWhere(
|
||||||
|
(f) => f.functionCode == selectedFunction,
|
||||||
|
orElse: () => DeviceFunctionData(
|
||||||
|
entityId: '',
|
||||||
|
functionCode: selectedFunction ?? '',
|
||||||
|
operationName: '',
|
||||||
|
value: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: 360,
|
width: 360,
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
@ -149,11 +160,17 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () => context.read<FunctionBloc>().add(
|
onTap: () => RoutineTapFunctionHelper.onTapFunction(
|
||||||
SelectFunction(
|
context,
|
||||||
functionCode: function.code,
|
functionCode: function.code,
|
||||||
operationName: function.operationName,
|
functionOperationName: function.operationName,
|
||||||
),
|
functionValueDescription: selectedFunctionData.valueDescription,
|
||||||
|
deviceUuid: widget.device?.uuid,
|
||||||
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
|
'dis_current',
|
||||||
|
'presence_time',
|
||||||
|
'illuminance_value',
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -36,7 +36,7 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
|||||||
dialogType: dialogType,
|
dialogType: dialogType,
|
||||||
sliderRange: sliderRange,
|
sliderRange: sliderRange,
|
||||||
displayedValue: getDisplayText,
|
displayedValue: getDisplayText,
|
||||||
initialValue: functionData.value ?? 250,
|
initialValue: functionData.value ?? 0.0,
|
||||||
onConditionChanged: (condition) => context.read<FunctionBloc>().add(
|
onConditionChanged: (condition) => context.read<FunctionBloc>().add(
|
||||||
AddFunction(
|
AddFunction(
|
||||||
functionData: DeviceFunctionData(
|
functionData: DeviceFunctionData(
|
||||||
@ -44,7 +44,7 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
|||||||
functionCode: selectedFunction,
|
functionCode: selectedFunction,
|
||||||
operationName: functionData.operationName,
|
operationName: functionData.operationName,
|
||||||
condition: condition,
|
condition: condition,
|
||||||
value: functionData.value,
|
value: functionData.value ?? 0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -87,7 +87,7 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
|||||||
final intValue = int.tryParse('${functionData.value ?? ''}');
|
final intValue = int.tryParse('${functionData.value ?? ''}');
|
||||||
return switch (functionData.functionCode) {
|
return switch (functionData.functionCode) {
|
||||||
'presence_time' => '${intValue ?? '0'}',
|
'presence_time' => '${intValue ?? '0'}',
|
||||||
'dis_current' => '${intValue ?? '250'}',
|
'dis_current' => '${intValue ?? '0'}',
|
||||||
'illuminance_value' => '${intValue ?? '0'}',
|
'illuminance_value' => '${intValue ?? '0'}',
|
||||||
_ => '$intValue',
|
_ => '$intValue',
|
||||||
};
|
};
|
||||||
|
@ -8,19 +8,20 @@ class CustomExpansionTileSpaceTree extends StatelessWidget {
|
|||||||
final bool isSelected;
|
final bool isSelected;
|
||||||
final bool isSoldCheck;
|
final bool isSoldCheck;
|
||||||
final bool isExpanded;
|
final bool isExpanded;
|
||||||
final Function? onExpansionChanged;
|
final void Function()? onExpansionChanged;
|
||||||
final Function? onItemSelected;
|
final void Function()? onItemSelected;
|
||||||
|
|
||||||
const CustomExpansionTileSpaceTree(
|
const CustomExpansionTileSpaceTree({
|
||||||
{super.key,
|
required this.isSelected,
|
||||||
this.spaceId,
|
|
||||||
required this.title,
|
required this.title,
|
||||||
|
this.spaceId,
|
||||||
this.children,
|
this.children,
|
||||||
this.isExpanded = false,
|
|
||||||
this.onExpansionChanged,
|
this.onExpansionChanged,
|
||||||
this.onItemSelected,
|
this.onItemSelected,
|
||||||
required this.isSelected,
|
this.isExpanded = false,
|
||||||
this.isSoldCheck = false});
|
this.isSoldCheck = false,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -30,50 +31,30 @@ class CustomExpansionTileSpaceTree extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Checkbox(
|
Checkbox(
|
||||||
value: isSoldCheck ? null : isSelected,
|
value: isSoldCheck ? null : isSelected,
|
||||||
onChanged: (bool? value) {
|
onChanged: (value) => onItemSelected?.call(),
|
||||||
if (onItemSelected != null) {
|
|
||||||
onItemSelected!();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tristate: true,
|
tristate: true,
|
||||||
side: WidgetStateBorderSide.resolveWith((states) {
|
side: WidgetStateBorderSide.resolveWith(
|
||||||
return const BorderSide(color: ColorsManager.grayBorder);
|
(states) => const BorderSide(color: ColorsManager.grayBorder),
|
||||||
}),
|
),
|
||||||
fillColor: WidgetStateProperty.resolveWith((states) {
|
fillColor: WidgetStateProperty.resolveWith((states) {
|
||||||
if (states.contains(WidgetState.selected)) {
|
if (states.contains(WidgetState.selected)) {
|
||||||
return ColorsManager.blue1;
|
return ColorsManager.blue1;
|
||||||
} else {
|
|
||||||
return ColorsManager.checkBoxFillColor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ColorsManager.checkBoxFillColor;
|
||||||
}),
|
}),
|
||||||
checkColor: ColorsManager.whiteColors,
|
checkColor: ColorsManager.whiteColors,
|
||||||
),
|
),
|
||||||
if (children != null && children!.isNotEmpty)
|
_buildExpansionIcon(),
|
||||||
InkWell(
|
|
||||||
onTap: () {
|
|
||||||
if (onExpansionChanged != null) {
|
|
||||||
onExpansionChanged!();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Icon(
|
|
||||||
isExpanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right,
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
size: 16.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: onItemSelected,
|
||||||
if (onItemSelected != null) {
|
|
||||||
onItemSelected!();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Text(
|
child: Text(
|
||||||
_capitalizeFirstLetter(title),
|
_capitalizeFirstLetter(title),
|
||||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.blackColor // Change color to black when selected
|
? ColorsManager.blackColor
|
||||||
: ColorsManager.lightGrayColor, // Gray when not selected
|
: ColorsManager.lightGrayColor,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -92,6 +73,20 @@ class CustomExpansionTileSpaceTree extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildExpansionIcon() {
|
||||||
|
return Visibility(
|
||||||
|
visible: children != null && children!.isNotEmpty,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onExpansionChanged,
|
||||||
|
child: Icon(
|
||||||
|
isExpanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right,
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
size: 16.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
String _capitalizeFirstLetter(String text) {
|
String _capitalizeFirstLetter(String text) {
|
||||||
if (text.isEmpty) return text;
|
if (text.isEmpty) return text;
|
||||||
return text[0].toUpperCase() + text.substring(1);
|
return text[0].toUpperCase() + text.substring(1);
|
||||||
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:syncrow_web/common/widgets/search_bar.dart';
|
import 'package:syncrow_web/common/widgets/search_bar.dart';
|
||||||
|
import 'package:syncrow_web/common/widgets/sidebar_communities_list.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_state.dart';
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_state.dart';
|
||||||
@ -23,7 +24,13 @@ class SpaceTreeView extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SpaceTreeViewState extends State<SpaceTreeView> {
|
class _SpaceTreeViewState extends State<SpaceTreeView> {
|
||||||
final ScrollController _scrollController = ScrollController();
|
late final ScrollController _scrollController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_scrollController = ScrollController();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@ -34,7 +41,7 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(builder: (context, state) {
|
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(builder: (context, state) {
|
||||||
List<CommunityModel> list =
|
final communities =
|
||||||
state.searchQuery.isNotEmpty ? state.filteredCommunity : state.communityList;
|
state.searchQuery.isNotEmpty ? state.filteredCommunity : state.communityList;
|
||||||
return Container(
|
return Container(
|
||||||
height: MediaQuery.sizeOf(context).height,
|
height: MediaQuery.sizeOf(context).height,
|
||||||
@ -45,12 +52,14 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: Column(
|
: Column(
|
||||||
children: [
|
children: [
|
||||||
widget.isSide == true
|
if (widget.isSide == true)
|
||||||
? Container(
|
Container(
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: ColorsManager.circleRolesBackground,
|
color: ColorsManager.circleRolesBackground,
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topRight: Radius.circular(20), topLeft: Radius.circular(20)),
|
topRight: Radius.circular(20),
|
||||||
|
topLeft: Radius.circular(20),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
@ -59,15 +68,21 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
borderRadius: const BorderRadius.all(
|
||||||
border: Border.all(color: ColorsManager.grayBorder)),
|
Radius.circular(20),
|
||||||
|
),
|
||||||
|
border: Border.all(
|
||||||
|
color: ColorsManager.grayBorder,
|
||||||
|
),
|
||||||
|
),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
style: context.textTheme.bodyMedium
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
?.copyWith(color: ColorsManager.blackColor),
|
color: ColorsManager.blackColor,
|
||||||
onChanged: (value) {
|
),
|
||||||
context.read<SpaceTreeBloc>().add(SearchQueryEvent(value));
|
onChanged: (value) => context.read<SpaceTreeBloc>().add(
|
||||||
},
|
SearchQueryEvent(value),
|
||||||
decoration: textBoxDecoration(radios: 20)!.copyWith(
|
),
|
||||||
|
decoration: textBoxDecoration(radios: 20)?.copyWith(
|
||||||
fillColor: Colors.white,
|
fillColor: Colors.white,
|
||||||
suffixIcon: Padding(
|
suffixIcon: Padding(
|
||||||
padding: const EdgeInsets.only(right: 16),
|
padding: const EdgeInsets.only(right: 16),
|
||||||
@ -80,7 +95,8 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
hintStyle: context.textTheme.bodyMedium?.copyWith(
|
hintStyle: context.textTheme.bodyMedium?.copyWith(
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: ColorsManager.textGray),
|
color: ColorsManager.textGray,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -89,170 +105,91 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: CustomSearchBar(
|
else
|
||||||
onSearchChanged: (query) {
|
CustomSearchBar(
|
||||||
context.read<SpaceTreeBloc>().add(SearchQueryEvent(query));
|
onSearchChanged: (query) => context.read<SpaceTreeBloc>().add(
|
||||||
},
|
SearchQueryEvent(query),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: state.isSearching
|
child: state.isSearching
|
||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: ListView(
|
: SidebarCommunitiesList(
|
||||||
shrinkWrap: true,
|
onScrollToEnd: () {
|
||||||
scrollDirection: Axis.horizontal,
|
if (!state.paginationIsLoading) {
|
||||||
children: [
|
context.read<SpaceTreeBloc>().add(
|
||||||
Container(
|
PaginationEvent(
|
||||||
width: MediaQuery.sizeOf(context).width * 0.5,
|
state.paginationModel,
|
||||||
padding: const EdgeInsets.all(8.0),
|
state.communityList,
|
||||||
child: list.isEmpty
|
|
||||||
? Center(
|
|
||||||
child: Text(
|
|
||||||
'No results found',
|
|
||||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
)
|
|
||||||
: Scrollbar(
|
|
||||||
scrollbarOrientation: ScrollbarOrientation.left,
|
|
||||||
thumbVisibility: true,
|
|
||||||
controller: _scrollController,
|
|
||||||
child: NotificationListener(
|
|
||||||
onNotification: (notification) {
|
|
||||||
if (notification is ScrollEndNotification &&
|
|
||||||
notification.metrics.extentAfter == 0) {
|
|
||||||
// If the user has reached the end of the list Load more data
|
|
||||||
context.read<SpaceTreeBloc>().add(PaginationEvent(
|
|
||||||
state.paginationModel, state.communityList));
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
child: Padding(
|
scrollController: _scrollController,
|
||||||
padding: const EdgeInsets.only(left: 16),
|
communities: communities,
|
||||||
child: ListView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
itemCount: list.length,
|
|
||||||
controller: _scrollController,
|
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return CustomExpansionTileSpaceTree(
|
return CustomExpansionTileSpaceTree(
|
||||||
title: list[index].name,
|
title: communities[index].name,
|
||||||
isSelected: state.selectedCommunities
|
isSelected:
|
||||||
.contains(list[index].uuid),
|
state.selectedCommunities.contains(communities[index].uuid),
|
||||||
isSoldCheck: state.selectedCommunities
|
isSoldCheck:
|
||||||
.contains(list[index].uuid),
|
state.selectedCommunities.contains(communities[index].uuid),
|
||||||
onExpansionChanged: () {
|
onExpansionChanged: () => context.read<SpaceTreeBloc>().add(
|
||||||
context.read<SpaceTreeBloc>().add(
|
OnCommunityExpanded(
|
||||||
OnCommunityExpanded(list[index].uuid));
|
communities[index].uuid,
|
||||||
},
|
),
|
||||||
isExpanded: state.expandedCommunities
|
),
|
||||||
.contains(list[index].uuid),
|
isExpanded: state.expandedCommunities.contains(
|
||||||
|
communities[index].uuid,
|
||||||
|
),
|
||||||
onItemSelected: () {
|
onItemSelected: () {
|
||||||
context.read<SpaceTreeBloc>().add(
|
context.read<SpaceTreeBloc>().add(
|
||||||
OnCommunitySelected(list[index].uuid,
|
OnCommunitySelected(
|
||||||
list[index].spaces));
|
communities[index].uuid,
|
||||||
|
communities[index].spaces,
|
||||||
|
),
|
||||||
|
);
|
||||||
widget.onSelect();
|
widget.onSelect();
|
||||||
},
|
},
|
||||||
children: list[index].spaces.map((space) {
|
children: communities[index].spaces.map(
|
||||||
|
(space) {
|
||||||
return CustomExpansionTileSpaceTree(
|
return CustomExpansionTileSpaceTree(
|
||||||
title: space.name,
|
title: space.name,
|
||||||
isExpanded: state.expandedSpaces
|
isExpanded: state.expandedSpaces.contains(space.uuid),
|
||||||
.contains(space.uuid),
|
|
||||||
onItemSelected: () {
|
onItemSelected: () {
|
||||||
context.read<SpaceTreeBloc>().add(
|
context.read<SpaceTreeBloc>().add(
|
||||||
OnSpaceSelected(
|
OnSpaceSelected(
|
||||||
list[index],
|
communities[index],
|
||||||
space.uuid ?? '',
|
space.uuid ?? '',
|
||||||
space.children));
|
space.children,
|
||||||
|
),
|
||||||
|
);
|
||||||
widget.onSelect();
|
widget.onSelect();
|
||||||
},
|
},
|
||||||
onExpansionChanged: () {
|
onExpansionChanged: () => context.read<SpaceTreeBloc>().add(
|
||||||
context.read<SpaceTreeBloc>().add(
|
OnSpaceExpanded(
|
||||||
OnSpaceExpanded(list[index].uuid,
|
communities[index].uuid,
|
||||||
space.uuid ?? ''));
|
space.uuid ?? '',
|
||||||
},
|
),
|
||||||
isSelected: state.selectedSpaces
|
),
|
||||||
.contains(space.uuid) ||
|
isSelected: state.selectedSpaces.contains(space.uuid) ||
|
||||||
state.soldCheck.contains(space.uuid),
|
|
||||||
isSoldCheck:
|
|
||||||
state.soldCheck.contains(space.uuid),
|
state.soldCheck.contains(space.uuid),
|
||||||
|
isSoldCheck: state.soldCheck.contains(space.uuid),
|
||||||
children: _buildNestedSpaces(
|
children: _buildNestedSpaces(
|
||||||
context, state, space, list[index]),
|
context,
|
||||||
|
state,
|
||||||
|
space,
|
||||||
|
communities[index],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}).toList(),
|
},
|
||||||
|
).toList(),
|
||||||
);
|
);
|
||||||
}),
|
},
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (state.paginationIsLoading) const CircularProgressIndicator(),
|
if (state.paginationIsLoading) const CircularProgressIndicator(),
|
||||||
// Expanded(
|
|
||||||
// child: Padding(
|
|
||||||
// padding: const EdgeInsets.all(8.0),
|
|
||||||
// child: list.isEmpty
|
|
||||||
// ? Center(
|
|
||||||
// child: Text(
|
|
||||||
// 'No results found',
|
|
||||||
// style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
|
||||||
// color: ColorsManager.lightGrayColor, // Gray when not selected
|
|
||||||
// fontWeight: FontWeight.w400,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// )
|
|
||||||
// : ListView(
|
|
||||||
// shrinkWrap: true,
|
|
||||||
// children: list
|
|
||||||
// .map(
|
|
||||||
// (community) => CustomExpansionTileSpaceTree(
|
|
||||||
// title: community.name,
|
|
||||||
// isSelected:
|
|
||||||
// state.selectedCommunities.contains(community.uuid),
|
|
||||||
// isSoldCheck:
|
|
||||||
// state.selectedCommunities.contains(community.uuid),
|
|
||||||
// onExpansionChanged: () {
|
|
||||||
// context
|
|
||||||
// .read<SpaceTreeBloc>()
|
|
||||||
// .add(OnCommunityExpanded(community.uuid));
|
|
||||||
// },
|
|
||||||
// isExpanded:
|
|
||||||
// state.expandedCommunities.contains(community.uuid),
|
|
||||||
// onItemSelected: () {
|
|
||||||
// context.read<SpaceTreeBloc>().add(
|
|
||||||
// OnCommunitySelected(community.uuid, community.spaces));
|
|
||||||
|
|
||||||
// onSelect();
|
|
||||||
// },
|
|
||||||
// children: community.spaces.map((space) {
|
|
||||||
// return CustomExpansionTileSpaceTree(
|
|
||||||
// title: space.name,
|
|
||||||
// isExpanded: state.expandedSpaces.contains(space.uuid),
|
|
||||||
// onItemSelected: () {
|
|
||||||
// context.read<SpaceTreeBloc>().add(OnSpaceSelected(
|
|
||||||
// community.uuid, space.uuid ?? '', space.children));
|
|
||||||
// onSelect();
|
|
||||||
// },
|
|
||||||
// onExpansionChanged: () {
|
|
||||||
// context.read<SpaceTreeBloc>().add(
|
|
||||||
// OnSpaceExpanded(community.uuid, space.uuid ?? ''));
|
|
||||||
// },
|
|
||||||
// isSelected: state.selectedSpaces.contains(space.uuid) ||
|
|
||||||
// state.soldCheck.contains(space.uuid),
|
|
||||||
// isSoldCheck: state.soldCheck.contains(space.uuid),
|
|
||||||
// children: _buildNestedSpaces(
|
|
||||||
// context, state, space, community.uuid),
|
|
||||||
// );
|
|
||||||
// }).toList(),
|
|
||||||
// ),
|
|
||||||
// )
|
|
||||||
// .toList(),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -260,7 +197,11 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildNestedSpaces(
|
List<Widget> _buildNestedSpaces(
|
||||||
BuildContext context, SpaceTreeState state, SpaceModel space, CommunityModel community) {
|
BuildContext context,
|
||||||
|
SpaceTreeState state,
|
||||||
|
SpaceModel space,
|
||||||
|
CommunityModel community,
|
||||||
|
) {
|
||||||
return space.children.map((child) {
|
return space.children.map((child) {
|
||||||
return CustomExpansionTileSpaceTree(
|
return CustomExpansionTileSpaceTree(
|
||||||
isSelected:
|
isSelected:
|
||||||
@ -269,13 +210,15 @@ class _SpaceTreeViewState extends State<SpaceTreeView> {
|
|||||||
title: child.name,
|
title: child.name,
|
||||||
isExpanded: state.expandedSpaces.contains(child.uuid),
|
isExpanded: state.expandedSpaces.contains(child.uuid),
|
||||||
onItemSelected: () {
|
onItemSelected: () {
|
||||||
context
|
context.read<SpaceTreeBloc>().add(
|
||||||
.read<SpaceTreeBloc>()
|
OnSpaceSelected(community, child.uuid ?? '', child.children),
|
||||||
.add(OnSpaceSelected(community, child.uuid ?? '', child.children));
|
);
|
||||||
widget.onSelect();
|
widget.onSelect();
|
||||||
},
|
},
|
||||||
onExpansionChanged: () {
|
onExpansionChanged: () {
|
||||||
context.read<SpaceTreeBloc>().add(OnSpaceExpanded(community.uuid, child.uuid ?? ''));
|
context.read<SpaceTreeBloc>().add(
|
||||||
|
OnSpaceExpanded(community.uuid, child.uuid ?? ''),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
children: _buildNestedSpaces(context, state, child, community),
|
children: _buildNestedSpaces(context, state, child, community),
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
// Flutter imports
|
// Flutter imports
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
@ -336,6 +338,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
}
|
}
|
||||||
spaces.add(newSpace);
|
spaces.add(newSpace);
|
||||||
_updateNodePosition(newSpace, newSpace.position);
|
_updateNodePosition(newSpace, newSpace.position);
|
||||||
|
realignTree();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -450,7 +453,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
|
|
||||||
void _saveSpaces() {
|
void _saveSpaces() {
|
||||||
if (widget.selectedCommunity == null) {
|
if (widget.selectedCommunity == null) {
|
||||||
debugPrint("No community selected for saving spaces.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -530,35 +532,83 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Offset getBalancedChildPosition(SpaceModel parent) {
|
Offset getBalancedChildPosition(SpaceModel parent) {
|
||||||
int totalSiblings = parent.children.length + 1;
|
const double nodeWidth = 200;
|
||||||
double totalWidth = (totalSiblings - 1) * 250; // Horizontal spacing
|
const double verticalGap = 180;
|
||||||
|
|
||||||
|
if (parent.children.isEmpty) {
|
||||||
|
// First child → exactly center
|
||||||
|
return Offset(parent.position.dx, parent.position.dy + verticalGap);
|
||||||
|
} else {
|
||||||
|
// More children → arrange them spaced horizontally
|
||||||
|
double totalWidth = (parent.children.length) * (nodeWidth + 60);
|
||||||
double startX = parent.position.dx - (totalWidth / 2);
|
double startX = parent.position.dx - (totalWidth / 2);
|
||||||
|
|
||||||
Offset position = Offset(startX + (parent.children.length * 250), parent.position.dy + 180);
|
double childX = startX + (parent.children.length * (nodeWidth + 60));
|
||||||
|
return Offset(childX, parent.position.dy + verticalGap);
|
||||||
// Check for overlaps & adjust
|
|
||||||
while (spaces.any((s) => (s.position - position).distance < 250)) {
|
|
||||||
position = Offset(position.dx + 250, position.dy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return position;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void realignTree() {
|
void realignTree() {
|
||||||
void updatePositions(SpaceModel node, double x, double y) {
|
const double nodeWidth = 200;
|
||||||
node.position = Offset(x, y);
|
const double nodeHeight = 100;
|
||||||
|
const double horizontalGap = 60;
|
||||||
|
const double verticalGap = 180;
|
||||||
|
const double rootGap = 400; // extra space between different roots
|
||||||
|
|
||||||
int numChildren = node.children.length;
|
double canvasRightEdge = 1000;
|
||||||
double childStartX = x - ((numChildren - 1) * 250) / 2;
|
double canvasBottomEdge = 1000;
|
||||||
|
|
||||||
for (int i = 0; i < numChildren; i++) {
|
double calculateSubtreeWidth(SpaceModel node) {
|
||||||
updatePositions(node.children[i], childStartX + (i * 250), y + 180);
|
if (node.children.isEmpty) return nodeWidth;
|
||||||
|
double totalWidth = 0;
|
||||||
|
for (var child in node.children) {
|
||||||
|
totalWidth += calculateSubtreeWidth(child) + horizontalGap;
|
||||||
|
}
|
||||||
|
return totalWidth - horizontalGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void layoutSubtree(SpaceModel node, double startX, double y) {
|
||||||
|
double subtreeWidth = calculateSubtreeWidth(node);
|
||||||
|
double centerX = startX + subtreeWidth / 2 - nodeWidth / 2;
|
||||||
|
node.position = Offset(centerX, y);
|
||||||
|
|
||||||
|
canvasRightEdge = max(canvasRightEdge, centerX + nodeWidth);
|
||||||
|
canvasBottomEdge = max(canvasBottomEdge, y + nodeHeight);
|
||||||
|
|
||||||
|
if (node.children.length == 1) {
|
||||||
|
final child = node.children.first;
|
||||||
|
layoutSubtree(child, centerX, y + verticalGap);
|
||||||
|
} else {
|
||||||
|
double childX = startX;
|
||||||
|
for (var child in node.children) {
|
||||||
|
double childWidth = calculateSubtreeWidth(child);
|
||||||
|
layoutSubtree(child, childX, y + verticalGap);
|
||||||
|
childX += childWidth + horizontalGap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spaces.isNotEmpty) {
|
// ⚡ New: layout each root separately
|
||||||
updatePositions(spaces.first, spaces.first.position.dx, spaces.first.position.dy);
|
final List<SpaceModel> roots = spaces
|
||||||
|
.where((s) =>
|
||||||
|
s.parent == null &&
|
||||||
|
s.status != SpaceStatus.deleted &&
|
||||||
|
s.status != SpaceStatus.parentDeleted)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
double currentX = 100; // start some margin from left
|
||||||
|
double currentY = 100; // top margin
|
||||||
|
|
||||||
|
for (var root in roots) {
|
||||||
|
layoutSubtree(root, currentX, currentY);
|
||||||
|
double rootWidth = calculateSubtreeWidth(root);
|
||||||
|
currentX += rootWidth + rootGap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
canvasWidth = canvasRightEdge + 400;
|
||||||
|
canvasHeight = canvasBottomEdge + 400;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onDuplicate(BuildContext parentContext) {
|
void _onDuplicate(BuildContext parentContext) {
|
||||||
@ -642,63 +692,19 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _duplicateSpace(SpaceModel space) {
|
void _duplicateSpace(SpaceModel space) {
|
||||||
final Map<SpaceModel, SpaceModel> originalToDuplicate = {};
|
final double horizontalGap = 250.0;
|
||||||
double horizontalGap = 250.0; // Increased spacing
|
final double verticalGap = 180.0;
|
||||||
double verticalGap = 180.0; // Adjusted for better visualization
|
final double nodeWidth = 200;
|
||||||
|
final double nodeHeight = 100;
|
||||||
|
final double breathingSpace = 300.0; // extra gap after original tree
|
||||||
|
|
||||||
print("🟢 Duplicating: ${space.name}");
|
/// Helper to recursively duplicate a node and its children
|
||||||
|
|
||||||
/// **Find a new position ensuring no overlap**
|
|
||||||
Offset getBalancedChildPosition(SpaceModel parent) {
|
|
||||||
int totalSiblings = parent.children.length + 1;
|
|
||||||
double totalWidth = (totalSiblings - 1) * horizontalGap;
|
|
||||||
double startX = parent.position.dx - (totalWidth / 2);
|
|
||||||
Offset position = Offset(
|
|
||||||
startX + (parent.children.length * horizontalGap), parent.position.dy + verticalGap);
|
|
||||||
|
|
||||||
// **Check for overlaps & adjust**
|
|
||||||
while (spaces.any((s) => (s.position - position).distance < horizontalGap)) {
|
|
||||||
position = Offset(position.dx + horizontalGap, position.dy);
|
|
||||||
}
|
|
||||||
|
|
||||||
print("🔹 New position for ${parent.name}: (${position.dx}, ${position.dy})");
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// **Realign the entire tree after duplication**
|
|
||||||
void realignTree() {
|
|
||||||
void updatePositions(SpaceModel node, double x, double y) {
|
|
||||||
node.position = Offset(x, y);
|
|
||||||
print("✅ Adjusted ${node.name} to (${x}, ${y})");
|
|
||||||
|
|
||||||
int numChildren = node.children.length;
|
|
||||||
double childStartX = x - ((numChildren - 1) * horizontalGap) / 2;
|
|
||||||
|
|
||||||
for (int i = 0; i < numChildren; i++) {
|
|
||||||
updatePositions(node.children[i], childStartX + (i * horizontalGap), y + verticalGap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spaces.isNotEmpty) {
|
|
||||||
print("🔄 Realigning tree...");
|
|
||||||
updatePositions(spaces.first, spaces.first.position.dx, spaces.first.position.dy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// **Recursive duplication logic**
|
|
||||||
SpaceModel duplicateRecursive(SpaceModel original, SpaceModel? duplicatedParent) {
|
SpaceModel duplicateRecursive(SpaceModel original, SpaceModel? duplicatedParent) {
|
||||||
Offset newPosition = duplicatedParent == null
|
|
||||||
? Offset(original.position.dx + horizontalGap, original.position.dy)
|
|
||||||
: getBalancedChildPosition(duplicatedParent);
|
|
||||||
|
|
||||||
final duplicatedName = SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
final duplicatedName = SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
||||||
print(
|
|
||||||
"🟡 Duplicating ${original.name} → ${duplicatedName} at (${newPosition.dx}, ${newPosition.dy})");
|
|
||||||
|
|
||||||
final duplicated = SpaceModel(
|
final duplicated = SpaceModel(
|
||||||
name: duplicatedName,
|
name: duplicatedName,
|
||||||
icon: original.icon,
|
icon: original.icon,
|
||||||
position: newPosition,
|
position: Offset.zero,
|
||||||
isPrivate: original.isPrivate,
|
isPrivate: original.isPrivate,
|
||||||
children: [],
|
children: [],
|
||||||
status: SpaceStatus.newSpace,
|
status: SpaceStatus.newSpace,
|
||||||
@ -708,9 +714,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
tags: original.tags,
|
tags: original.tags,
|
||||||
);
|
);
|
||||||
|
|
||||||
setState(() {
|
|
||||||
spaces.add(duplicated);
|
spaces.add(duplicated);
|
||||||
_updateNodePosition(duplicated, duplicated.position);
|
|
||||||
|
|
||||||
if (duplicatedParent != null) {
|
if (duplicatedParent != null) {
|
||||||
final newConnection = Connection(
|
final newConnection = Connection(
|
||||||
@ -722,14 +726,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
duplicated.incomingConnection = newConnection;
|
duplicated.incomingConnection = newConnection;
|
||||||
duplicatedParent.addOutgoingConnection(newConnection);
|
duplicatedParent.addOutgoingConnection(newConnection);
|
||||||
duplicatedParent.children.add(duplicated);
|
duplicatedParent.children.add(duplicated);
|
||||||
print("🔗 Created connection: ${duplicatedParent.name} → ${duplicated.name}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// **Recalculate the whole tree to avoid overlaps**
|
|
||||||
realignTree();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Recursively duplicate children
|
|
||||||
for (var child in original.children) {
|
for (var child in original.children) {
|
||||||
duplicateRecursive(child, duplicated);
|
duplicateRecursive(child, duplicated);
|
||||||
}
|
}
|
||||||
@ -737,21 +735,49 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
return duplicated;
|
return duplicated;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// **Handle root duplication**
|
/// Layout a subtree rooted at node
|
||||||
if (space.parent == null) {
|
void layoutSubtree(SpaceModel node, double startX, double startY) {
|
||||||
print("🟠 Duplicating root node: ${space.name}");
|
double calculateSubtreeWidth(SpaceModel n) {
|
||||||
SpaceModel duplicatedRoot = duplicateRecursive(space, null);
|
if (n.children.isEmpty) return nodeWidth;
|
||||||
|
double width = 0;
|
||||||
setState(() {
|
for (var child in n.children) {
|
||||||
spaces.add(duplicatedRoot);
|
width += calculateSubtreeWidth(child) + horizontalGap;
|
||||||
realignTree();
|
}
|
||||||
});
|
return width - horizontalGap;
|
||||||
|
|
||||||
print("✅ Root duplication successful: ${duplicatedRoot.name}");
|
|
||||||
} else {
|
|
||||||
duplicateRecursive(space, space.parent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
print("🟢 Finished duplication process for: ${space.name}");
|
void assignPositions(SpaceModel n, double x, double y) {
|
||||||
|
double subtreeWidth = calculateSubtreeWidth(n);
|
||||||
|
double centerX = x + subtreeWidth / 2 - nodeWidth / 2;
|
||||||
|
n.position = Offset(centerX, y);
|
||||||
|
|
||||||
|
if (n.children.length == 1) {
|
||||||
|
assignPositions(n.children.first, centerX, y + verticalGap);
|
||||||
|
} else {
|
||||||
|
double childX = x;
|
||||||
|
for (var child in n.children) {
|
||||||
|
double childWidth = calculateSubtreeWidth(child);
|
||||||
|
assignPositions(child, childX, y + verticalGap);
|
||||||
|
childX += childWidth + horizontalGap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double totalSubtreeWidth = calculateSubtreeWidth(node);
|
||||||
|
assignPositions(node, startX, startY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Actual duplication process
|
||||||
|
setState(() {
|
||||||
|
if (space.parent == null) {
|
||||||
|
// Duplicating a ROOT node
|
||||||
|
SpaceModel duplicatedRoot = duplicateRecursive(space, null);
|
||||||
|
realignTree();
|
||||||
|
} else {
|
||||||
|
// Duplicating a CHILD node inside its parent
|
||||||
|
SpaceModel duplicated = duplicateRecursive(space, space.parent);
|
||||||
|
realignTree();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -592,8 +592,6 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
return CreateSubSpaceDialog(
|
return CreateSubSpaceDialog(
|
||||||
spaceName: name,
|
spaceName: name,
|
||||||
dialogTitle: isEdit ? 'Edit Sub-space' : 'Create Sub-space',
|
dialogTitle: isEdit ? 'Edit Sub-space' : 'Create Sub-space',
|
||||||
spaceTags: spaceTags,
|
|
||||||
isEdit: isEdit,
|
|
||||||
products: products,
|
products: products,
|
||||||
existingSubSpaces: existingSubSpaces,
|
existingSubSpaces: existingSubSpaces,
|
||||||
onSave: (slectedSubspaces) {
|
onSave: (slectedSubspaces) {
|
||||||
|
@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
@ -107,6 +109,11 @@ class _LoadedSpaceViewState extends State<LoadedSpaceView> {
|
|||||||
selectedSpaceUuid: widget.selectedSpace?.uuid ??
|
selectedSpaceUuid: widget.selectedSpace?.uuid ??
|
||||||
widget.selectedCommunity?.uuid ??
|
widget.selectedCommunity?.uuid ??
|
||||||
'',
|
'',
|
||||||
|
onCreateCommunity: (name, description) {
|
||||||
|
context.read<SpaceManagementBloc>().add(
|
||||||
|
CreateCommunityEvent(name, description, context),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
CommunityStructureArea(
|
CommunityStructureArea(
|
||||||
selectedCommunity: widget.selectedCommunity,
|
selectedCommunity: widget.selectedCommunity,
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
|
class SidebarAddCommunityButton extends StatelessWidget {
|
||||||
|
const SidebarAddCommunityButton({
|
||||||
|
required this.onTap,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final void Function() onTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox.square(
|
||||||
|
dimension: 30,
|
||||||
|
child: IconButton(
|
||||||
|
style: IconButton.styleFrom(
|
||||||
|
iconSize: 20,
|
||||||
|
backgroundColor: ColorsManager.circleImageBackground,
|
||||||
|
shape: const CircleBorder(
|
||||||
|
side: BorderSide(
|
||||||
|
color: ColorsManager.lightGrayBorderColor,
|
||||||
|
width: 3,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: onTap,
|
||||||
|
icon: SvgPicture.asset(Assets.addIcon),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/sidebar_add_community_button.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
|
|
||||||
|
class SidebarHeader extends StatelessWidget {
|
||||||
|
const SidebarHeader({
|
||||||
|
required this.onAddCommunity,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final void Function() onAddCommunity;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
decoration: subSectionContainerDecoration,
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Communities',
|
||||||
|
style: context.textTheme.titleMedium?.copyWith(
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SidebarAddCommunityButton(
|
||||||
|
onTap: onAddCommunity,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,161 +1,135 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:syncrow_web/common/widgets/empty_search_result_widget.dart';
|
||||||
import 'package:syncrow_web/common/widgets/search_bar.dart';
|
import 'package:syncrow_web/common/widgets/search_bar.dart';
|
||||||
|
import 'package:syncrow_web/common/widgets/sidebar_communities_list.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_tile.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_tile.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/sidebar_header.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_tile_widget.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_tile_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/create_community/view/create_community_dialog.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_bloc.dart';
|
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_event.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
|
|
||||||
|
import '../../../space_tree/bloc/space_tree_event.dart';
|
||||||
|
|
||||||
class SidebarWidget extends StatefulWidget {
|
class SidebarWidget extends StatefulWidget {
|
||||||
final List<CommunityModel> communities;
|
final List<CommunityModel> communities;
|
||||||
final String? selectedSpaceUuid;
|
final String? selectedSpaceUuid;
|
||||||
|
final void Function(String name, String description) onCreateCommunity;
|
||||||
|
|
||||||
const SidebarWidget({
|
const SidebarWidget({
|
||||||
super.key,
|
|
||||||
required this.communities,
|
required this.communities,
|
||||||
|
required this.onCreateCommunity,
|
||||||
this.selectedSpaceUuid,
|
this.selectedSpaceUuid,
|
||||||
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_SidebarWidgetState createState() => _SidebarWidgetState();
|
State<SidebarWidget> createState() => _SidebarWidgetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SidebarWidgetState extends State<SidebarWidget> {
|
class _SidebarWidgetState extends State<SidebarWidget> {
|
||||||
String _searchQuery = ''; // Track search query
|
late final ScrollController _scrollController;
|
||||||
|
|
||||||
|
String _searchQuery = '';
|
||||||
String? _selectedSpaceUuid;
|
String? _selectedSpaceUuid;
|
||||||
String? _selectedId;
|
String? _selectedId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
_scrollController = ScrollController();
|
||||||
|
_scrollController.addListener(_onScroll);
|
||||||
|
_selectedId = widget.selectedSpaceUuid;
|
||||||
super.initState();
|
super.initState();
|
||||||
_selectedId = widget.selectedSpaceUuid; // Initialize with the passed selected space UUID
|
}
|
||||||
|
|
||||||
|
void _onScroll() {
|
||||||
|
if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 100) {
|
||||||
|
// Trigger pagination event
|
||||||
|
final bloc = context.read<SpaceTreeBloc>();
|
||||||
|
if (!bloc.state.paginationIsLoading && bloc.state.paginationModel?.hasNext == true) {
|
||||||
|
bloc.add(
|
||||||
|
PaginationEvent(
|
||||||
|
bloc.state.paginationModel!,
|
||||||
|
bloc.state.communityList,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.removeListener(_onScroll);
|
||||||
|
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant SidebarWidget oldWidget) {
|
void didUpdateWidget(covariant SidebarWidget oldWidget) {
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
if (widget.selectedSpaceUuid != oldWidget.selectedSpaceUuid) {
|
if (widget.selectedSpaceUuid != oldWidget.selectedSpaceUuid) {
|
||||||
setState(() {
|
setState(() => _selectedId = widget.selectedSpaceUuid);
|
||||||
_selectedId = widget.selectedSpaceUuid;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
// Function to filter communities based on the search query
|
|
||||||
List<CommunityModel> _filterCommunities() {
|
|
||||||
if (_searchQuery.isEmpty) {
|
|
||||||
// Reset the selected community and space UUIDs if there's no query
|
|
||||||
_selectedSpaceUuid = null;
|
|
||||||
return widget.communities;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter communities and expand only those that match the query
|
|
||||||
return widget.communities.where((community) {
|
|
||||||
final containsQueryInCommunity =
|
|
||||||
community.name.toLowerCase().contains(_searchQuery.toLowerCase());
|
|
||||||
final containsQueryInSpaces =
|
|
||||||
community.spaces.any((space) => _containsQuery(space, _searchQuery.toLowerCase()));
|
|
||||||
|
|
||||||
return containsQueryInCommunity || containsQueryInSpaces;
|
|
||||||
}).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to determine if any space or its children match the search query
|
|
||||||
bool _containsQuery(SpaceModel space, String query) {
|
|
||||||
final matchesSpace = space.name.toLowerCase().contains(query);
|
|
||||||
final matchesChildren =
|
|
||||||
space.children.any((child) => _containsQuery(child, query)); // Recursive check for children
|
|
||||||
|
|
||||||
// If the space or any of its children match the query, expand this space
|
|
||||||
if (matchesSpace || matchesChildren) {
|
|
||||||
_selectedSpaceUuid = space.uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchesSpace || matchesChildren;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isSpaceOrChildSelected(SpaceModel space) {
|
bool _isSpaceOrChildSelected(SpaceModel space) {
|
||||||
// Return true if the current space or any of its child spaces is selected
|
final isSpaceSelected = _selectedSpaceUuid == space.uuid;
|
||||||
if (_selectedSpaceUuid == space.uuid) {
|
final anySubSpaceIsSelected = space.children.any(_isSpaceOrChildSelected);
|
||||||
return true;
|
return isSpaceSelected || anySubSpaceIsSelected;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively check if any child spaces match the query
|
static const _width = 300.0;
|
||||||
for (var child in space.children) {
|
|
||||||
if (_isSpaceOrChildSelected(child)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final filteredCommunities = _filterCommunities();
|
final spaceTreeState = context.watch<SpaceTreeBloc>().state;
|
||||||
|
final filteredCommunities = spaceTreeState.isSearching
|
||||||
|
? spaceTreeState.filteredCommunity
|
||||||
|
: spaceTreeState.communityList;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
width: 300,
|
width: _width,
|
||||||
decoration: subSectionContainerDecoration,
|
decoration: subSectionContainerDecoration,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min, // Ensures the Column only takes necessary height
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
// Communities title with the add button
|
SidebarHeader(onAddCommunity: _onAddCommunity),
|
||||||
Container(
|
|
||||||
decoration: subSectionContainerDecoration,
|
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text('Communities',
|
|
||||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
|
||||||
color: ColorsManager.blackColor,
|
|
||||||
)),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () => _navigateToBlank(context),
|
|
||||||
child: Container(
|
|
||||||
width: 30,
|
|
||||||
height: 30,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: ColorsManager.whiteColors,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: SvgPicture.asset(
|
|
||||||
Assets.roundedAddIcon,
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Search bar
|
|
||||||
CustomSearchBar(
|
CustomSearchBar(
|
||||||
onSearchChanged: (query) {
|
onSearchChanged: (query) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_searchQuery = query;
|
_searchQuery = query;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
context.read<SpaceTreeBloc>().add(SearchQueryEvent(query));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
// Community list
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView(
|
child: Visibility(
|
||||||
children: filteredCommunities.map((community) {
|
visible: filteredCommunities.isNotEmpty,
|
||||||
return _buildCommunityTile(context, community);
|
replacement: const EmptySearchResultWidget(),
|
||||||
}).toList(),
|
child: SidebarCommunitiesList(
|
||||||
|
scrollController: _scrollController,
|
||||||
|
onScrollToEnd: () {},
|
||||||
|
communities: filteredCommunities,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index == filteredCommunities.length) {
|
||||||
|
return const Padding(
|
||||||
|
padding: EdgeInsets.all(8.0),
|
||||||
|
child: Center(child: CircularProgressIndicator()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _buildCommunityTile(context, filteredCommunities[index]);
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -163,18 +137,18 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _navigateToBlank(BuildContext context) {
|
|
||||||
setState(() {
|
|
||||||
_selectedId = '';
|
|
||||||
});
|
|
||||||
context.read<SpaceManagementBloc>().add(
|
|
||||||
NewCommunityEvent(communities: widget.communities),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildCommunityTile(BuildContext context, CommunityModel community) {
|
Widget _buildCommunityTile(BuildContext context, CommunityModel community) {
|
||||||
bool hasChildren = community.spaces.isNotEmpty;
|
final spaces = community.spaces
|
||||||
|
.where(
|
||||||
|
(space) =>
|
||||||
|
{
|
||||||
|
SpaceStatus.deleted,
|
||||||
|
SpaceStatus.parentDeleted,
|
||||||
|
}.contains(space.status) ==
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.map((space) => _buildSpaceTile(space: space, community: community))
|
||||||
|
.toList();
|
||||||
return CommunityTile(
|
return CommunityTile(
|
||||||
title: community.name,
|
title: community.name,
|
||||||
key: ValueKey(community.uuid),
|
key: ValueKey(community.uuid),
|
||||||
@ -183,7 +157,7 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
onItemSelected: () {
|
onItemSelected: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedId = community.uuid;
|
_selectedId = community.uuid;
|
||||||
_selectedSpaceUuid = null; // Update the selected community
|
_selectedSpaceUuid = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
context.read<CenterBodyBloc>().add(CommunitySelectedEvent());
|
context.read<CenterBodyBloc>().add(CommunitySelectedEvent());
|
||||||
@ -192,31 +166,25 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
SelectCommunityEvent(selectedCommunity: community),
|
SelectCommunityEvent(selectedCommunity: community),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onExpansionChanged: (String title, bool expanded) {
|
onExpansionChanged: (title, expanded) {},
|
||||||
_handleExpansionChange(community.uuid, expanded);
|
children: spaces,
|
||||||
},
|
|
||||||
children: hasChildren
|
|
||||||
? community.spaces
|
|
||||||
.where((space) => (space.status != SpaceStatus.deleted ||
|
|
||||||
space.status != SpaceStatus.parentDeleted))
|
|
||||||
.map((space) => _buildSpaceTile(space, community))
|
|
||||||
.toList()
|
|
||||||
: null,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSpaceTile(SpaceModel space, CommunityModel community, {int depth = 1}) {
|
Widget _buildSpaceTile({
|
||||||
bool isExpandedSpace = _isSpaceOrChildSelected(space);
|
required SpaceModel space,
|
||||||
|
required CommunityModel community,
|
||||||
|
}) {
|
||||||
|
final spaceIsExpanded = _isSpaceOrChildSelected(space);
|
||||||
|
final isSelected = _selectedId == space.uuid;
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.only(left: depth * 16.0),
|
padding: const EdgeInsetsDirectional.only(start: 16.0),
|
||||||
child: SpaceTile(
|
child: SpaceTile(
|
||||||
title: space.name,
|
title: space.name,
|
||||||
key: ValueKey(space.uuid),
|
key: ValueKey(space.uuid),
|
||||||
isSelected: _selectedId == space.uuid,
|
isSelected: isSelected,
|
||||||
initiallyExpanded: isExpandedSpace,
|
initiallyExpanded: spaceIsExpanded,
|
||||||
onExpansionChanged: (bool expanded) {
|
onExpansionChanged: (expanded) {},
|
||||||
_handleExpansionChange(space.uuid ?? '', expanded);
|
|
||||||
},
|
|
||||||
onItemSelected: () {
|
onItemSelected: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedId = space.uuid;
|
_selectedId = space.uuid;
|
||||||
@ -227,11 +195,36 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
SelectSpaceEvent(selectedCommunity: community, selectedSpace: space),
|
SelectSpaceEvent(selectedCommunity: community, selectedSpace: space),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
children: space.children.isNotEmpty
|
children: space.children
|
||||||
? space.children.map((childSpace) => _buildSpaceTile(childSpace, community)).toList()
|
.map(
|
||||||
: [], // Recursively render child spaces if available
|
(childSpace) => _buildSpaceTile(
|
||||||
));
|
space: childSpace,
|
||||||
|
community: community,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleExpansionChange(String uuid, bool expanded) {}
|
void _onAddCommunity() =>
|
||||||
|
_selectedId?.isNotEmpty ?? true ? _clearSelection() : _showCreateCommunityDialog();
|
||||||
|
|
||||||
|
void _clearSelection() {
|
||||||
|
setState(() => _selectedId = '');
|
||||||
|
context.read<SpaceManagementBloc>().add(
|
||||||
|
NewCommunityEvent(communities: widget.communities),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showCreateCommunityDialog() {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => CreateCommunityDialog(
|
||||||
|
isEditMode: false,
|
||||||
|
existingCommunityNames: widget.communities.map((e) => e.name).toList(),
|
||||||
|
onCreateCommunity: widget.onCreateCommunity,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,169 +4,155 @@ import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
|||||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart';
|
import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_event.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_state.dart';
|
import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/subspace_chip.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
class CreateSubSpaceDialog extends StatelessWidget {
|
class CreateSubSpaceDialog extends StatefulWidget {
|
||||||
final bool isEdit;
|
|
||||||
final String dialogTitle;
|
final String dialogTitle;
|
||||||
final List<SubspaceModel>? existingSubSpaces;
|
final List<SubspaceModel>? existingSubSpaces;
|
||||||
final String? spaceName;
|
final String? spaceName;
|
||||||
final List<Tag>? spaceTags;
|
|
||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
final Function(List<SubspaceModel>?)? onSave;
|
final void Function(List<SubspaceModel>?)? onSave;
|
||||||
|
|
||||||
const CreateSubSpaceDialog(
|
const CreateSubSpaceDialog({
|
||||||
{Key? key,
|
|
||||||
required this.isEdit,
|
|
||||||
required this.dialogTitle,
|
required this.dialogTitle,
|
||||||
this.existingSubSpaces,
|
|
||||||
required this.spaceName,
|
required this.spaceName,
|
||||||
required this.spaceTags,
|
|
||||||
required this.products,
|
required this.products,
|
||||||
required this.onSave})
|
required this.onSave,
|
||||||
: super(key: key);
|
this.existingSubSpaces,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CreateSubSpaceDialog> createState() => _CreateSubSpaceDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
||||||
|
late final TextEditingController _subspaceNameController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_subspaceNameController = TextEditingController();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_subspaceNameController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final screenWidth = MediaQuery.of(context).size.width;
|
return BlocProvider(
|
||||||
final textController = TextEditingController();
|
|
||||||
|
|
||||||
return Dialog(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
child: BlocProvider(
|
|
||||||
create: (_) {
|
create: (_) {
|
||||||
final bloc = SubSpaceBloc();
|
final bloc = SubSpaceBloc();
|
||||||
if (existingSubSpaces != null) {
|
if (widget.existingSubSpaces != null) {
|
||||||
for (var subSpace in existingSubSpaces!) {
|
for (final subSpace in widget.existingSubSpaces ?? []) {
|
||||||
bloc.add(AddSubSpace(subSpace));
|
bloc.add(AddSubSpace(subSpace));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bloc;
|
return bloc;
|
||||||
},
|
},
|
||||||
|
child: Dialog(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
child: BlocBuilder<SubSpaceBloc, SubSpaceState>(
|
child: BlocBuilder<SubSpaceBloc, SubSpaceState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return Container(
|
return Container(
|
||||||
|
width: context.screenWidth * 0.35,
|
||||||
color: ColorsManager.whiteColors,
|
color: ColorsManager.whiteColors,
|
||||||
child: SizedBox(
|
padding: const EdgeInsets.all(16),
|
||||||
width: screenWidth * 0.35,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
dialogTitle,
|
widget.dialogTitle,
|
||||||
style: Theme.of(context)
|
style: context.textTheme.headlineLarge?.copyWith(
|
||||||
.textTheme
|
color: ColorsManager.blackColor,
|
||||||
.headlineLarge
|
),
|
||||||
?.copyWith(color: ColorsManager.blackColor),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Container(
|
Container(
|
||||||
width: screenWidth * 0.35,
|
width: context.screenWidth * 0.35,
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 10.0, horizontal: 16.0),
|
vertical: 10,
|
||||||
|
horizontal: 16,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: ColorsManager.boxColor,
|
color: ColorsManager.boxColor,
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
spacing: 8.0,
|
spacing: 8,
|
||||||
runSpacing: 8.0,
|
runSpacing: 8,
|
||||||
|
alignment: WrapAlignment.start,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
...state.subSpaces.asMap().entries.map(
|
...state.subSpaces.asMap().entries.map(
|
||||||
(entry) {
|
(entry) {
|
||||||
final index = entry.key;
|
final index = entry.key;
|
||||||
final subSpace = entry.value;
|
final subSpace = entry.value;
|
||||||
|
|
||||||
final lowerName =
|
final lowerName = subSpace.subspaceName.toLowerCase();
|
||||||
subSpace.subspaceName.toLowerCase();
|
|
||||||
|
|
||||||
final duplicateIndices = state.subSpaces
|
final duplicateIndices = state.subSpaces
|
||||||
.asMap()
|
.asMap()
|
||||||
.entries
|
.entries
|
||||||
.where((e) =>
|
.where((e) =>
|
||||||
e.value.subspaceName.toLowerCase() ==
|
e.value.subspaceName.toLowerCase() == lowerName)
|
||||||
lowerName)
|
|
||||||
.map((e) => e.key)
|
.map((e) => e.key)
|
||||||
.toList();
|
.toList();
|
||||||
final isDuplicate =
|
final isDuplicate = duplicateIndices.length > 1 &&
|
||||||
duplicateIndices.length > 1 &&
|
|
||||||
duplicateIndices.indexOf(index) != 0;
|
duplicateIndices.indexOf(index) != 0;
|
||||||
|
return SubspaceChip(
|
||||||
return Chip(
|
subSpace: SubspaceTemplateModel(
|
||||||
label: Text(subSpace.subspaceName,
|
subspaceName: entry.value.subspaceName,
|
||||||
style: Theme.of(context)
|
disabled: entry.value.disabled,
|
||||||
.textTheme
|
|
||||||
.bodyMedium
|
|
||||||
?.copyWith(
|
|
||||||
color:
|
|
||||||
ColorsManager.spaceColor)),
|
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
side: BorderSide(
|
|
||||||
color: isDuplicate
|
|
||||||
? ColorsManager.red
|
|
||||||
: ColorsManager.transparentColor,
|
|
||||||
width: 0,
|
|
||||||
),
|
),
|
||||||
|
isDuplicate: isDuplicate,
|
||||||
|
onDeleted: () => context.read<SubSpaceBloc>().add(
|
||||||
|
RemoveSubSpace(subSpace),
|
||||||
),
|
),
|
||||||
deleteIcon: Container(
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
border: Border.all(
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
width: 1.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.close,
|
|
||||||
size: 16,
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onDeleted: () => context
|
|
||||||
.read<SubSpaceBloc>()
|
|
||||||
.add(RemoveSubSpace(subSpace)),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 200,
|
width: 200,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: textController,
|
controller: _subspaceNameController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
hintText: state.subSpaces.isEmpty
|
hintText: state.subSpaces.isEmpty
|
||||||
? 'Please enter the name'
|
? 'Please enter the name'
|
||||||
: null,
|
: null,
|
||||||
hintStyle: Theme.of(context)
|
hintStyle: context.textTheme.bodySmall?.copyWith(
|
||||||
.textTheme
|
color: ColorsManager.lightGrayColor,
|
||||||
.bodySmall
|
),
|
||||||
?.copyWith(
|
),
|
||||||
color: ColorsManager
|
|
||||||
.lightGrayColor)),
|
|
||||||
onSubmitted: (value) {
|
onSubmitted: (value) {
|
||||||
if (value.trim().isNotEmpty) {
|
final trimmedValue = value.trim();
|
||||||
|
if (trimmedValue.isNotEmpty) {
|
||||||
context.read<SubSpaceBloc>().add(
|
context.read<SubSpaceBloc>().add(
|
||||||
AddSubSpace(SubspaceModel(
|
AddSubSpace(
|
||||||
subspaceName: value.trim(),
|
SubspaceModel(
|
||||||
disabled: false)));
|
subspaceName: trimmedValue,
|
||||||
textController.clear();
|
disabled: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_subspaceNameController.clear();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
style:
|
style: context.textTheme.bodyMedium,
|
||||||
Theme.of(context).textTheme.bodyMedium),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -174,13 +160,12 @@ class CreateSubSpaceDialog extends StatelessWidget {
|
|||||||
if (state.errorMessage.isNotEmpty)
|
if (state.errorMessage.isNotEmpty)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: Text(state.errorMessage,
|
child: Text(
|
||||||
style: Theme.of(context)
|
state.errorMessage,
|
||||||
.textTheme
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
.bodySmall
|
|
||||||
?.copyWith(
|
|
||||||
color: ColorsManager.warningRed,
|
color: ColorsManager.warningRed,
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Row(
|
Row(
|
||||||
@ -196,16 +181,14 @@ class CreateSubSpaceDialog extends StatelessWidget {
|
|||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
onPressed: (state.errorMessage.isNotEmpty)
|
onPressed: state.errorMessage.isEmpty
|
||||||
? null
|
? () {
|
||||||
: () async {
|
final subSpacesBloc = context.read<SubSpaceBloc>();
|
||||||
final subSpaces = context
|
final subSpaces = subSpacesBloc.state.subSpaces;
|
||||||
.read<SubSpaceBloc>()
|
widget.onSave?.call(subSpaces);
|
||||||
.state
|
|
||||||
.subSpaces;
|
|
||||||
onSave!(subSpaces);
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
}
|
||||||
|
: null,
|
||||||
backgroundColor: ColorsManager.secondaryColor,
|
backgroundColor: ColorsManager.secondaryColor,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
foregroundColor: state.errorMessage.isNotEmpty
|
foregroundColor: state.errorMessage.isNotEmpty
|
||||||
@ -218,8 +201,7 @@ class CreateSubSpaceDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
));
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
|
||||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_bloc.dart';
|
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_event.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_state.dart';
|
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/create_subspace_model_chips_box.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/create_subspace_model_footer_buttons.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
class CreateSubSpaceModelDialog extends StatelessWidget {
|
class CreateSubSpaceModelDialog extends StatelessWidget {
|
||||||
final bool isEdit;
|
final bool isEdit;
|
||||||
@ -14,212 +15,68 @@ class CreateSubSpaceModelDialog extends StatelessWidget {
|
|||||||
final List<SubspaceTemplateModel>? existingSubSpaces;
|
final List<SubspaceTemplateModel>? existingSubSpaces;
|
||||||
final void Function(List<SubspaceTemplateModel> newSubspaces)? onUpdate;
|
final void Function(List<SubspaceTemplateModel> newSubspaces)? onUpdate;
|
||||||
|
|
||||||
const CreateSubSpaceModelDialog(
|
const CreateSubSpaceModelDialog({
|
||||||
{Key? key,
|
|
||||||
required this.isEdit,
|
required this.isEdit,
|
||||||
required this.dialogTitle,
|
required this.dialogTitle,
|
||||||
this.existingSubSpaces,
|
this.existingSubSpaces,
|
||||||
this.onUpdate})
|
this.onUpdate,
|
||||||
: super(key: key);
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final screenWidth = MediaQuery.of(context).size.width;
|
final screenWidth = MediaQuery.of(context).size.width;
|
||||||
final textController = TextEditingController();
|
|
||||||
|
|
||||||
return Dialog(
|
return Dialog(
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
),
|
),
|
||||||
child: BlocProvider(
|
child: BlocProvider(
|
||||||
create: (_) {
|
create: (context) {
|
||||||
final bloc = SubSpaceModelBloc();
|
final bloc = SubSpaceModelBloc();
|
||||||
if (existingSubSpaces != null) {
|
if (existingSubSpaces != null) {
|
||||||
for (var subSpace in existingSubSpaces!) {
|
for (final subSpace in existingSubSpaces ?? []) {
|
||||||
bloc.add(AddSubSpaceModel(subSpace));
|
bloc.add(AddSubSpaceModel(subSpace));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return bloc;
|
return bloc;
|
||||||
},
|
},
|
||||||
child: BlocBuilder<SubSpaceModelBloc, SubSpaceModelState>(
|
child: BlocBuilder<SubSpaceModelBloc, SubSpaceModelState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) => Container(
|
||||||
return Container(
|
|
||||||
color: ColorsManager.whiteColors,
|
color: ColorsManager.whiteColors,
|
||||||
child: SizedBox(
|
|
||||||
width: screenWidth * 0.3,
|
width: screenWidth * 0.3,
|
||||||
child: Padding(
|
padding: const EdgeInsets.all(16),
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
dialogTitle,
|
dialogTitle,
|
||||||
style: Theme.of(context)
|
style: context.textTheme.headlineLarge?.copyWith(
|
||||||
.textTheme
|
color: ColorsManager.blackColor,
|
||||||
.headlineLarge
|
),
|
||||||
?.copyWith(color: ColorsManager.blackColor),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Container(
|
CreateSubspaceModelChipsBox(subSpaces: state.subSpaces),
|
||||||
width: screenWidth * 0.35,
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 10.0, horizontal: 16.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: ColorsManager.boxColor,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Wrap(
|
|
||||||
spacing: 8.0,
|
|
||||||
runSpacing: 8.0,
|
|
||||||
children: [
|
|
||||||
...state.subSpaces.asMap().entries.map(
|
|
||||||
(entry) {
|
|
||||||
final index = entry.key;
|
|
||||||
final subSpace = entry.value;
|
|
||||||
|
|
||||||
final lowerName =
|
|
||||||
subSpace.subspaceName.toLowerCase();
|
|
||||||
|
|
||||||
final duplicateIndices = state.subSpaces
|
|
||||||
.asMap()
|
|
||||||
.entries
|
|
||||||
.where((e) =>
|
|
||||||
e.value.subspaceName.toLowerCase() ==
|
|
||||||
lowerName)
|
|
||||||
.map((e) => e.key)
|
|
||||||
.toList();
|
|
||||||
final isDuplicate =
|
|
||||||
duplicateIndices.length > 1 &&
|
|
||||||
duplicateIndices.indexOf(index) != 0;
|
|
||||||
|
|
||||||
return Chip(
|
|
||||||
label: Text(subSpace.subspaceName,
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall
|
|
||||||
?.copyWith(
|
|
||||||
color: ColorsManager.spaceColor,
|
|
||||||
)),
|
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
side: BorderSide(
|
|
||||||
color: isDuplicate
|
|
||||||
? ColorsManager.red
|
|
||||||
: ColorsManager.transparentColor,
|
|
||||||
width: 0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
deleteIcon: Container(
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
border: Border.all(
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
width: 1.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.close,
|
|
||||||
size: 16,
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onDeleted: () => context
|
|
||||||
.read<SubSpaceModelBloc>()
|
|
||||||
.add(RemoveSubSpaceModel(subSpace)),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 200,
|
|
||||||
child: TextField(
|
|
||||||
controller: textController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: InputBorder.none,
|
|
||||||
hintText: state.subSpaces.isEmpty
|
|
||||||
? 'Please enter the name'
|
|
||||||
: null,
|
|
||||||
hintStyle: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall!
|
|
||||||
.copyWith(
|
|
||||||
color: ColorsManager
|
|
||||||
.lightGrayColor)),
|
|
||||||
onSubmitted: (value) {
|
|
||||||
if (value.trim().isNotEmpty) {
|
|
||||||
context.read<SubSpaceModelBloc>().add(
|
|
||||||
AddSubSpaceModel(
|
|
||||||
SubspaceTemplateModel(
|
|
||||||
subspaceName: value.trim(),
|
|
||||||
disabled: false)));
|
|
||||||
textController.clear();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyMedium
|
|
||||||
?.copyWith(
|
|
||||||
color: ColorsManager.blackColor)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (state.errorMessage.isNotEmpty)
|
if (state.errorMessage.isNotEmpty)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 16.0),
|
padding: const EdgeInsets.only(bottom: 16),
|
||||||
child: Text(state.errorMessage,
|
child: Text(
|
||||||
style: Theme.of(context)
|
state.errorMessage,
|
||||||
.textTheme
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
.bodySmall
|
|
||||||
?.copyWith(
|
|
||||||
color: ColorsManager.red,
|
color: ColorsManager.red,
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Row(
|
CreateSubspaceModelFooterButtons(
|
||||||
children: [
|
onUpdate: onUpdate,
|
||||||
Expanded(
|
errorMessage: state.errorMessage,
|
||||||
child: CancelButton(
|
|
||||||
label: 'Cancel',
|
|
||||||
onPressed: () async {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: DefaultButton(
|
|
||||||
onPressed: (state.errorMessage.isNotEmpty)
|
|
||||||
? null
|
|
||||||
: () async {
|
|
||||||
final subSpaces = context
|
|
||||||
.read<SubSpaceModelBloc>()
|
|
||||||
.state
|
|
||||||
.subSpaces;
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
if (onUpdate != null) {
|
|
||||||
onUpdate!(subSpaces);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
backgroundColor: ColorsManager.secondaryColor,
|
|
||||||
borderRadius: 10,
|
|
||||||
foregroundColor: state.errorMessage.isNotEmpty
|
|
||||||
? ColorsManager.whiteColorsWithOpacity
|
|
||||||
: ColorsManager.whiteColors,
|
|
||||||
child: const Text('OK'),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/subspace_chip.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/subspaces_textfield.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class CreateSubspaceModelChipsBox extends StatelessWidget {
|
||||||
|
const CreateSubspaceModelChipsBox({
|
||||||
|
required this.subSpaces,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<SubspaceTemplateModel> subSpaces;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final screenWidth = MediaQuery.of(context).size.width;
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
width: screenWidth * 0.35,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 10,
|
||||||
|
horizontal: 16,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.boxColor,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Wrap(
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 8,
|
||||||
|
alignment: WrapAlignment.start,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
|
children: [
|
||||||
|
...subSpaces.asMap().entries.map(
|
||||||
|
(entry) {
|
||||||
|
final index = entry.key;
|
||||||
|
final subSpace = entry.value;
|
||||||
|
|
||||||
|
final lowerName = subSpace.subspaceName.toLowerCase();
|
||||||
|
|
||||||
|
final duplicateIndices = subSpaces
|
||||||
|
.asMap()
|
||||||
|
.entries
|
||||||
|
.where((e) => e.value.subspaceName.toLowerCase() == lowerName)
|
||||||
|
.map((e) => e.key)
|
||||||
|
.toList();
|
||||||
|
final isDuplicate = duplicateIndices.length > 1 &&
|
||||||
|
duplicateIndices.indexOf(index) != 0;
|
||||||
|
|
||||||
|
return SubspaceChip(
|
||||||
|
subSpace: subSpace,
|
||||||
|
isDuplicate: isDuplicate,
|
||||||
|
onDeleted: () => context.read<SubSpaceModelBloc>().add(
|
||||||
|
RemoveSubSpaceModel(subSpace),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SubspacesTextfield(
|
||||||
|
hintText: subSpaces.isEmpty ? 'Please enter the name' : null,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class CreateSubspaceModelFooterButtons extends StatelessWidget {
|
||||||
|
const CreateSubspaceModelFooterButtons({
|
||||||
|
required this.onUpdate,
|
||||||
|
required this.errorMessage,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final void Function(List<SubspaceTemplateModel> newSubspaces)? onUpdate;
|
||||||
|
final String errorMessage;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: CancelButton(
|
||||||
|
label: 'Cancel',
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: DefaultButton(
|
||||||
|
onPressed: errorMessage.isEmpty
|
||||||
|
? () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
if (onUpdate != null) {
|
||||||
|
final subSpaces =
|
||||||
|
context.read<SubSpaceModelBloc>().state.subSpaces;
|
||||||
|
onUpdate!(subSpaces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
backgroundColor: ColorsManager.secondaryColor,
|
||||||
|
borderRadius: 10,
|
||||||
|
foregroundColor: errorMessage.isNotEmpty
|
||||||
|
? ColorsManager.whiteColorsWithOpacity
|
||||||
|
: ColorsManager.whiteColors,
|
||||||
|
child: const Text('OK'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class SubspaceChip extends StatelessWidget {
|
||||||
|
const SubspaceChip({
|
||||||
|
required this.subSpace,
|
||||||
|
required this.isDuplicate,
|
||||||
|
super.key,
|
||||||
|
required this.onDeleted,
|
||||||
|
});
|
||||||
|
|
||||||
|
final SubspaceTemplateModel subSpace;
|
||||||
|
final bool isDuplicate;
|
||||||
|
final void Function() onDeleted;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Chip(
|
||||||
|
label: Text(
|
||||||
|
subSpace.subspaceName,
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: isDuplicate ? ColorsManager.red : ColorsManager.spaceColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
side: BorderSide(
|
||||||
|
color: isDuplicate ? ColorsManager.red : ColorsManager.transparentColor,
|
||||||
|
width: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
deleteIcon: Container(
|
||||||
|
padding: const EdgeInsetsDirectional.all(1),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
width: 1.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
child: Icon(
|
||||||
|
Icons.close,
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onDeleted: onDeleted,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class SubspacesTextfield extends StatefulWidget {
|
||||||
|
const SubspacesTextfield({
|
||||||
|
required this.hintText,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String? hintText;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SubspacesTextfield> createState() => _SubspacesTextfieldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SubspacesTextfieldState extends State<SubspacesTextfield> {
|
||||||
|
late final TextEditingController _controller;
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
_controller = TextEditingController();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
width: 100,
|
||||||
|
child: TextField(
|
||||||
|
controller: _controller,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
hintText: widget.hintText,
|
||||||
|
hintStyle: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onSubmitted: (value) {
|
||||||
|
final trimmedValue = value.trim();
|
||||||
|
if (trimmedValue.isNotEmpty) {
|
||||||
|
context.read<SubSpaceModelBloc>().add(
|
||||||
|
AddSubSpaceModel(
|
||||||
|
SubspaceTemplateModel(
|
||||||
|
subspaceName: trimmedValue,
|
||||||
|
disabled: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_controller.clear();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -145,13 +145,11 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
TagChipDisplay(
|
TagChipDisplay(
|
||||||
context,
|
|
||||||
screenWidth: screenWidth,
|
|
||||||
spaceModel: updatedSpaceModel,
|
spaceModel: updatedSpaceModel,
|
||||||
products: products,
|
products: products,
|
||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
allTags: allTags,
|
allTags: allTags,
|
||||||
spaceNameController: spaceNameController,
|
spaceName: spaceNameController.text,
|
||||||
pageContext: pageContext,
|
pageContext: pageContext,
|
||||||
otherSpaceModels: otherSpaceModels,
|
otherSpaceModels: otherSpaceModels,
|
||||||
allSpaceModels: allSpaceModels,
|
allSpaceModels: allSpaceModels,
|
||||||
|
@ -10,90 +10,77 @@ import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_
|
|||||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/button_content_widget.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/button_content_widget.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart';
|
import 'package:syncrow_web/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
class TagChipDisplay extends StatelessWidget {
|
class TagChipDisplay extends StatelessWidget {
|
||||||
final double screenWidth;
|
const TagChipDisplay({
|
||||||
|
required this.spaceModel,
|
||||||
|
required this.products,
|
||||||
|
required this.subspaces,
|
||||||
|
required this.allTags,
|
||||||
|
required this.spaceName,
|
||||||
|
required this.projectTags,
|
||||||
|
this.pageContext,
|
||||||
|
this.otherSpaceModels,
|
||||||
|
this.allSpaceModels,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
final SpaceTemplateModel? spaceModel;
|
final SpaceTemplateModel? spaceModel;
|
||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
final List<SubspaceTemplateModel>? subspaces;
|
final List<SubspaceTemplateModel>? subspaces;
|
||||||
final List<String>? allTags;
|
final List<String>? allTags;
|
||||||
final TextEditingController spaceNameController;
|
final String spaceName;
|
||||||
final BuildContext? pageContext;
|
final BuildContext? pageContext;
|
||||||
final List<String>? otherSpaceModels;
|
final List<String>? otherSpaceModels;
|
||||||
final List<SpaceTemplateModel>? allSpaceModels;
|
final List<SpaceTemplateModel>? allSpaceModels;
|
||||||
final List<Tag> projectTags;
|
final List<Tag> projectTags;
|
||||||
|
|
||||||
const TagChipDisplay(BuildContext context,
|
Map<ProductModel, int> get _groupedTags {
|
||||||
{Key? key,
|
final spaceTags = spaceModel?.tags ?? <Tag>[];
|
||||||
required this.screenWidth,
|
|
||||||
required this.spaceModel,
|
final subspaces = spaceModel?.subspaceModels ?? [];
|
||||||
required this.products,
|
final subspaceTags = subspaces.expand((e) => e.tags ?? <Tag>[]).toList();
|
||||||
required this.subspaces,
|
|
||||||
required this.allTags,
|
return TagHelper.groupTags([...spaceTags, ...subspaceTags]);
|
||||||
required this.spaceNameController,
|
}
|
||||||
this.pageContext,
|
|
||||||
this.otherSpaceModels,
|
|
||||||
this.allSpaceModels,
|
|
||||||
required this.projectTags})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return (spaceModel?.tags?.isNotEmpty == true ||
|
final hasTags = spaceModel?.tags?.isNotEmpty ?? false;
|
||||||
spaceModel?.subspaceModels?.any((subspace) => subspace.tags?.isNotEmpty == true) ==
|
final hasSubspaceTags =
|
||||||
true)
|
spaceModel?.subspaceModels?.any((e) => e.tags?.isNotEmpty ?? false) ?? false;
|
||||||
? SizedBox(
|
|
||||||
width: screenWidth * 0.25,
|
if (hasTags || hasSubspaceTags) {
|
||||||
child: Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(8.0),
|
width: context.screenWidth * 0.25,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: ColorsManager.textFieldGreyColor,
|
color: ColorsManager.textFieldGreyColor,
|
||||||
borderRadius: BorderRadius.circular(15),
|
borderRadius: BorderRadius.circular(15),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: ColorsManager.textFieldGreyColor,
|
color: ColorsManager.textFieldGreyColor,
|
||||||
width: 3.0, // Border width
|
width: 3,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
spacing: 8.0,
|
spacing: 8,
|
||||||
runSpacing: 8.0,
|
runSpacing: 8,
|
||||||
children: [
|
children: [
|
||||||
// Combine tags from spaceModel and subspaces
|
..._groupedTags.entries.map((entry) => _buildChip(context, entry)),
|
||||||
...TagHelper.groupTags([
|
_buildEditChip(context),
|
||||||
...?spaceModel?.tags,
|
],
|
||||||
...?spaceModel?.subspaceModels?.expand((subspace) => subspace.tags ?? [])
|
|
||||||
]).entries.map(
|
|
||||||
(entry) => Chip(
|
|
||||||
avatar: SizedBox(
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
child: SvgPicture.asset(
|
|
||||||
entry.key.icon ?? 'assets/icons/gateway.svg',
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
label: Text(
|
}
|
||||||
'x${entry.value}', // Show count
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall!
|
|
||||||
.copyWith(color: ColorsManager.spaceColor),
|
|
||||||
),
|
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
side: const BorderSide(
|
|
||||||
color: ColorsManager.spaceColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
EditChip(onTap: () async {
|
|
||||||
// Use the Navigator's context for showDialog
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
|
|
||||||
await showDialog<bool>(
|
return _buildAddDevicesButton(context);
|
||||||
barrierDismissible: false,
|
}
|
||||||
|
|
||||||
|
Widget _buildEditChip(BuildContext context) {
|
||||||
|
return EditChip(
|
||||||
|
onTap: () => showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AssignTagModelsDialog(
|
builder: (context) => AssignTagModelsDialog(
|
||||||
products: products,
|
products: products,
|
||||||
@ -104,38 +91,64 @@ class TagChipDisplay extends StatelessWidget {
|
|||||||
spaceModel: spaceModel,
|
spaceModel: spaceModel,
|
||||||
otherSpaceModels: otherSpaceModels,
|
otherSpaceModels: otherSpaceModels,
|
||||||
initialTags: TagHelper.generateInitialTags(
|
initialTags: TagHelper.generateInitialTags(
|
||||||
subspaces: subspaces, spaceTagModels: spaceModel?.tags ?? []),
|
subspaces: subspaces,
|
||||||
|
spaceTagModels: spaceModel?.tags ?? [],
|
||||||
|
),
|
||||||
title: 'Edit Device',
|
title: 'Edit Device',
|
||||||
addedProducts: TagHelper.createInitialSelectedProducts(
|
addedProducts: TagHelper.createInitialSelectedProducts(
|
||||||
spaceModel?.tags ?? [], subspaces),
|
spaceModel?.tags ?? [],
|
||||||
|
subspaces,
|
||||||
|
),
|
||||||
spaceName: spaceModel?.modelName ?? '',
|
spaceName: spaceModel?.modelName ?? '',
|
||||||
projectTags: projectTags,
|
projectTags: projectTags,
|
||||||
));
|
|
||||||
})
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
);
|
||||||
: TextButton(
|
}
|
||||||
onPressed: () async {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
|
|
||||||
await showDialog<bool>(
|
Widget _buildChip(
|
||||||
barrierDismissible: false,
|
BuildContext context,
|
||||||
|
MapEntry<ProductModel, int> entry,
|
||||||
|
) {
|
||||||
|
return Chip(
|
||||||
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
side: const BorderSide(
|
||||||
|
color: ColorsManager.spaceColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
avatar: SvgPicture.asset(
|
||||||
|
entry.key.icon ?? Assets.gateway,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
height: 24,
|
||||||
|
width: 24,
|
||||||
|
),
|
||||||
|
label: Text(
|
||||||
|
'${entry.value}',
|
||||||
|
style: context.textTheme.bodySmall!.copyWith(
|
||||||
|
color: ColorsManager.spaceColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildAddDevicesButton(BuildContext context) {
|
||||||
|
return TextButton(
|
||||||
|
onPressed: () => showDialog<void>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AddDeviceTypeModelWidget(
|
builder: (context) => AddDeviceTypeModelWidget(
|
||||||
products: products,
|
products: products,
|
||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
allTags: allTags,
|
allTags: allTags,
|
||||||
spaceName: spaceNameController.text,
|
spaceName: spaceName,
|
||||||
pageContext: pageContext,
|
pageContext: pageContext,
|
||||||
isCreate: true,
|
isCreate: true,
|
||||||
spaceModel: spaceModel,
|
spaceModel: spaceModel,
|
||||||
otherSpaceModels: otherSpaceModels,
|
otherSpaceModels: otherSpaceModels,
|
||||||
projectTags: projectTags,
|
projectTags: projectTags,
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
),
|
),
|
||||||
|
85
lib/services/batch_control_devices_service.dart
Normal file
85
lib/services/batch_control_devices_service.dart
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||||
|
|
||||||
|
abstract interface class BatchControlDevicesService {
|
||||||
|
Future<bool> batchControlDevices({
|
||||||
|
required List<String> uuids,
|
||||||
|
required String code,
|
||||||
|
required Object value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
final class RemoteBatchControlDevicesService implements BatchControlDevicesService {
|
||||||
|
@override
|
||||||
|
Future<bool> batchControlDevices({
|
||||||
|
required List<String> uuids,
|
||||||
|
required String code,
|
||||||
|
required Object value,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final body = {
|
||||||
|
'devicesUuid': uuids,
|
||||||
|
'code': code,
|
||||||
|
'value': value,
|
||||||
|
'operationType': 'COMMAND',
|
||||||
|
};
|
||||||
|
|
||||||
|
final response = await HTTPService().post(
|
||||||
|
path: ApiEndpoints.deviceBatchControl,
|
||||||
|
body: body,
|
||||||
|
showServerMessage: true,
|
||||||
|
expectedResponseModel: (json) => (json['success'] as bool?) ?? false,
|
||||||
|
);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
log('Error fetching $e', name: 'BatchControlDevicesService');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class DebouncedBatchControlDevicesService
|
||||||
|
implements BatchControlDevicesService {
|
||||||
|
final BatchControlDevicesService decoratee;
|
||||||
|
final Duration debounceDuration;
|
||||||
|
|
||||||
|
final _pendingRequests = <(List<String> uuids, String code, Object value)>[];
|
||||||
|
var _isProcessing = false;
|
||||||
|
|
||||||
|
DebouncedBatchControlDevicesService({
|
||||||
|
required this.decoratee,
|
||||||
|
this.debounceDuration = const Duration(milliseconds: 800),
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> batchControlDevices({
|
||||||
|
required List<String> uuids,
|
||||||
|
required String code,
|
||||||
|
required Object value,
|
||||||
|
}) async {
|
||||||
|
_pendingRequests.add((uuids, code, value));
|
||||||
|
|
||||||
|
if (_isProcessing) return false;
|
||||||
|
|
||||||
|
_isProcessing = true;
|
||||||
|
|
||||||
|
await Future.delayed(debounceDuration);
|
||||||
|
|
||||||
|
final lastRequest = _pendingRequests.last;
|
||||||
|
_pendingRequests.clear();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final (lastRequestUuids, lastRequestCode, lastRequestValue) = lastRequest;
|
||||||
|
return decoratee.batchControlDevices(
|
||||||
|
uuids: lastRequestUuids,
|
||||||
|
code: lastRequestCode,
|
||||||
|
value: lastRequestValue,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
_isProcessing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
lib/services/control_device_service.dart
Normal file
75
lib/services/control_device_service.dart
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||||
|
|
||||||
|
abstract interface class ControlDeviceService {
|
||||||
|
Future<bool> controlDevice({
|
||||||
|
required String deviceUuid,
|
||||||
|
required Status status,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
final class RemoteControlDeviceService implements ControlDeviceService {
|
||||||
|
@override
|
||||||
|
Future<bool> controlDevice({
|
||||||
|
required String deviceUuid,
|
||||||
|
required Status status,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final response = await HTTPService().post(
|
||||||
|
path: ApiEndpoints.deviceControl.replaceAll('{uuid}', deviceUuid),
|
||||||
|
body: status.toMap(),
|
||||||
|
showServerMessage: true,
|
||||||
|
expectedResponseModel: (json) {
|
||||||
|
return (json['success'] as bool?) ?? false;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
log('Error fetching $e', name: 'ControlDeviceService');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class DebouncedControlDeviceService implements ControlDeviceService {
|
||||||
|
final ControlDeviceService decoratee;
|
||||||
|
final Duration debounceDuration;
|
||||||
|
|
||||||
|
DebouncedControlDeviceService({
|
||||||
|
required this.decoratee,
|
||||||
|
this.debounceDuration = const Duration(milliseconds: 800),
|
||||||
|
});
|
||||||
|
|
||||||
|
final _pendingRequests = <(String deviceUuid, Status status)>[];
|
||||||
|
var _isProcessing = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> controlDevice({
|
||||||
|
required String deviceUuid,
|
||||||
|
required Status status,
|
||||||
|
}) async {
|
||||||
|
_pendingRequests.add((deviceUuid, status));
|
||||||
|
|
||||||
|
if (_isProcessing) return false;
|
||||||
|
|
||||||
|
_isProcessing = true;
|
||||||
|
|
||||||
|
await Future.delayed(debounceDuration);
|
||||||
|
|
||||||
|
final lastRequest = _pendingRequests.last;
|
||||||
|
_pendingRequests.clear();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final (lastRequestDeviceUuid, lastRequestStatus) = lastRequest;
|
||||||
|
return decoratee.controlDevice(
|
||||||
|
deviceUuid: lastRequestDeviceUuid,
|
||||||
|
status: lastRequestStatus,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
_isProcessing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -321,13 +321,14 @@ class DevicesManagementApi {
|
|||||||
Future<bool> factoryReset(FactoryResetModel factoryReset, String uuid) async {
|
Future<bool> factoryReset(FactoryResetModel factoryReset, String uuid) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().post(
|
final response = await HTTPService().post(
|
||||||
path: ApiEndpoints.factoryReset.replaceAll('{deviceUuid}', uuid),
|
path: ApiEndpoints.factoryReset,
|
||||||
body: factoryReset.toMap(),
|
body: factoryReset.toMap(),
|
||||||
showServerMessage: true,
|
showServerMessage: true,
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
return json['success'] ?? false;
|
return json['success'] ?? false;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Error fetching $e');
|
debugPrint('Error fetching $e');
|
||||||
|
@ -13,8 +13,7 @@ class Assets {
|
|||||||
static const String rightLine = "assets/images/right_line.png";
|
static const String rightLine = "assets/images/right_line.png";
|
||||||
static const String google = "assets/images/google.svg";
|
static const String google = "assets/images/google.svg";
|
||||||
static const String facebook = "assets/images/facebook.svg";
|
static const String facebook = "assets/images/facebook.svg";
|
||||||
static const String invisiblePassword =
|
static const String invisiblePassword = "assets/images/Password_invisible.svg";
|
||||||
"assets/images/Password_invisible.svg";
|
|
||||||
static const String visiblePassword = "assets/images/password_visible.svg";
|
static const String visiblePassword = "assets/images/password_visible.svg";
|
||||||
static const String accessIcon = "assets/images/access_icon.svg";
|
static const String accessIcon = "assets/images/access_icon.svg";
|
||||||
static const String spaseManagementIcon =
|
static const String spaseManagementIcon =
|
||||||
@ -31,8 +30,7 @@ class Assets {
|
|||||||
static const String emptyTable = "assets/images/empty_table.svg";
|
static const String emptyTable = "assets/images/empty_table.svg";
|
||||||
|
|
||||||
// General assets
|
// General assets
|
||||||
static const String motionlessDetection =
|
static const String motionlessDetection = "assets/icons/motionless_detection.svg";
|
||||||
"assets/icons/motionless_detection.svg";
|
|
||||||
static const String acHeating = "assets/icons/ac_heating.svg";
|
static const String acHeating = "assets/icons/ac_heating.svg";
|
||||||
static const String acPowerOff = "assets/icons/ac_power_off.svg";
|
static const String acPowerOff = "assets/icons/ac_power_off.svg";
|
||||||
static const String acFanMiddle = "assets/icons/ac_fan_middle.svg";
|
static const String acFanMiddle = "assets/icons/ac_fan_middle.svg";
|
||||||
@ -69,22 +67,19 @@ class Assets {
|
|||||||
"assets/icons/automation_functions/temp_password_unlock.svg";
|
"assets/icons/automation_functions/temp_password_unlock.svg";
|
||||||
static const String doorlockNormalOpen =
|
static const String doorlockNormalOpen =
|
||||||
"assets/icons/automation_functions/doorlock_normal_open.svg";
|
"assets/icons/automation_functions/doorlock_normal_open.svg";
|
||||||
static const String doorbell =
|
static const String doorbell = "assets/icons/automation_functions/doorbell.svg";
|
||||||
"assets/icons/automation_functions/doorbell.svg";
|
|
||||||
static const String remoteUnlockViaApp =
|
static const String remoteUnlockViaApp =
|
||||||
"assets/icons/automation_functions/remote_unlock_via_app.svg";
|
"assets/icons/automation_functions/remote_unlock_via_app.svg";
|
||||||
static const String doubleLock =
|
static const String doubleLock =
|
||||||
"assets/icons/automation_functions/double_lock.svg";
|
"assets/icons/automation_functions/double_lock.svg";
|
||||||
static const String selfTestResult =
|
static const String selfTestResult =
|
||||||
"assets/icons/automation_functions/self_test_result.svg";
|
"assets/icons/automation_functions/self_test_result.svg";
|
||||||
static const String lockAlarm =
|
static const String lockAlarm = "assets/icons/automation_functions/lock_alarm.svg";
|
||||||
"assets/icons/automation_functions/lock_alarm.svg";
|
|
||||||
static const String presenceState =
|
static const String presenceState =
|
||||||
"assets/icons/automation_functions/presence_state.svg";
|
"assets/icons/automation_functions/presence_state.svg";
|
||||||
static const String currentTemp =
|
static const String currentTemp =
|
||||||
"assets/icons/automation_functions/current_temp.svg";
|
"assets/icons/automation_functions/current_temp.svg";
|
||||||
static const String presence =
|
static const String presence = "assets/icons/automation_functions/presence.svg";
|
||||||
"assets/icons/automation_functions/presence.svg";
|
|
||||||
static const String residualElectricity =
|
static const String residualElectricity =
|
||||||
"assets/icons/automation_functions/residual_electricity.svg";
|
"assets/icons/automation_functions/residual_electricity.svg";
|
||||||
static const String hijackAlarm =
|
static const String hijackAlarm =
|
||||||
@ -101,15 +96,12 @@ class Assets {
|
|||||||
|
|
||||||
// Presence Sensor Assets
|
// Presence Sensor Assets
|
||||||
static const String sensorMotionIcon = "assets/icons/sensor_motion_ic.svg";
|
static const String sensorMotionIcon = "assets/icons/sensor_motion_ic.svg";
|
||||||
static const String sensorPresenceIcon =
|
static const String sensorPresenceIcon = "assets/icons/sensor_presence_ic.svg";
|
||||||
"assets/icons/sensor_presence_ic.svg";
|
|
||||||
static const String sensorVacantIcon = "assets/icons/sensor_vacant_ic.svg";
|
static const String sensorVacantIcon = "assets/icons/sensor_vacant_ic.svg";
|
||||||
static const String illuminanceRecordIcon =
|
static const String illuminanceRecordIcon =
|
||||||
"assets/icons/illuminance_record_ic.svg";
|
"assets/icons/illuminance_record_ic.svg";
|
||||||
static const String presenceRecordIcon =
|
static const String presenceRecordIcon = "assets/icons/presence_record_ic.svg";
|
||||||
"assets/icons/presence_record_ic.svg";
|
static const String helpDescriptionIcon = "assets/icons/help_description_ic.svg";
|
||||||
static const String helpDescriptionIcon =
|
|
||||||
"assets/icons/help_description_ic.svg";
|
|
||||||
|
|
||||||
static const String lightPulp = "assets/icons/light_pulb.svg";
|
static const String lightPulp = "assets/icons/light_pulb.svg";
|
||||||
static const String acDevice = "assets/icons/ac_device.svg";
|
static const String acDevice = "assets/icons/ac_device.svg";
|
||||||
@ -159,12 +151,10 @@ class Assets {
|
|||||||
static const String unit = 'assets/icons/unit_icon.svg';
|
static const String unit = 'assets/icons/unit_icon.svg';
|
||||||
static const String villa = 'assets/icons/villa_icon.svg';
|
static const String villa = 'assets/icons/villa_icon.svg';
|
||||||
static const String iconEdit = 'assets/icons/icon_edit_icon.svg';
|
static const String iconEdit = 'assets/icons/icon_edit_icon.svg';
|
||||||
static const String textFieldSearch =
|
static const String textFieldSearch = 'assets/icons/textfield_search_icon.svg';
|
||||||
'assets/icons/textfield_search_icon.svg';
|
|
||||||
static const String roundedAddIcon = 'assets/icons/rounded_add_icon.svg';
|
static const String roundedAddIcon = 'assets/icons/rounded_add_icon.svg';
|
||||||
static const String addIcon = 'assets/icons/add_icon.svg';
|
static const String addIcon = 'assets/icons/add_icon.svg';
|
||||||
static const String smartThermostatIcon =
|
static const String smartThermostatIcon = 'assets/icons/smart_thermostat_icon.svg';
|
||||||
'assets/icons/smart_thermostat_icon.svg';
|
|
||||||
static const String smartLightIcon = 'assets/icons/smart_light_icon.svg';
|
static const String smartLightIcon = 'assets/icons/smart_light_icon.svg';
|
||||||
static const String presenceSensor = 'assets/icons/presence_sensor.svg';
|
static const String presenceSensor = 'assets/icons/presence_sensor.svg';
|
||||||
static const String Gang3SwitchIcon = 'assets/icons/3_Gang_switch_icon.svg';
|
static const String Gang3SwitchIcon = 'assets/icons/3_Gang_switch_icon.svg';
|
||||||
@ -212,8 +202,7 @@ class Assets {
|
|||||||
//assets/icons/water_leak_normal.svg
|
//assets/icons/water_leak_normal.svg
|
||||||
static const String waterLeakNormal = 'assets/icons/water_leak_normal.svg';
|
static const String waterLeakNormal = 'assets/icons/water_leak_normal.svg';
|
||||||
//assets/icons/water_leak_detected.svg
|
//assets/icons/water_leak_detected.svg
|
||||||
static const String waterLeakDetected =
|
static const String waterLeakDetected = 'assets/icons/water_leak_detected.svg';
|
||||||
'assets/icons/water_leak_detected.svg';
|
|
||||||
|
|
||||||
//assets/icons/automation_records.svg
|
//assets/icons/automation_records.svg
|
||||||
static const String automationRecords = 'assets/icons/automation_records.svg';
|
static const String automationRecords = 'assets/icons/automation_records.svg';
|
||||||
@ -284,16 +273,13 @@ class Assets {
|
|||||||
"assets/icons/functions_icons/sensitivity.svg";
|
"assets/icons/functions_icons/sensitivity.svg";
|
||||||
static const String assetsSensitivityOperationIcon =
|
static const String assetsSensitivityOperationIcon =
|
||||||
"assets/icons/functions_icons/sesitivity_operation_icon.svg";
|
"assets/icons/functions_icons/sesitivity_operation_icon.svg";
|
||||||
static const String assetsAcPower =
|
static const String assetsAcPower = "assets/icons/functions_icons/ac_power.svg";
|
||||||
"assets/icons/functions_icons/ac_power.svg";
|
|
||||||
static const String assetsAcPowerOFF =
|
static const String assetsAcPowerOFF =
|
||||||
"assets/icons/functions_icons/ac_power_off.svg";
|
"assets/icons/functions_icons/ac_power_off.svg";
|
||||||
static const String assetsChildLock =
|
static const String assetsChildLock =
|
||||||
"assets/icons/functions_icons/child_lock.svg";
|
"assets/icons/functions_icons/child_lock.svg";
|
||||||
static const String assetsFreezing =
|
static const String assetsFreezing = "assets/icons/functions_icons/freezing.svg";
|
||||||
"assets/icons/functions_icons/freezing.svg";
|
static const String assetsFanSpeed = "assets/icons/functions_icons/fan_speed.svg";
|
||||||
static const String assetsFanSpeed =
|
|
||||||
"assets/icons/functions_icons/fan_speed.svg";
|
|
||||||
static const String assetsAcCooling =
|
static const String assetsAcCooling =
|
||||||
"assets/icons/functions_icons/ac_cooling.svg";
|
"assets/icons/functions_icons/ac_cooling.svg";
|
||||||
static const String assetsAcHeating =
|
static const String assetsAcHeating =
|
||||||
@ -302,8 +288,7 @@ class Assets {
|
|||||||
"assets/icons/functions_icons/celsius_degrees.svg";
|
"assets/icons/functions_icons/celsius_degrees.svg";
|
||||||
static const String assetsTempreture =
|
static const String assetsTempreture =
|
||||||
"assets/icons/functions_icons/tempreture.svg";
|
"assets/icons/functions_icons/tempreture.svg";
|
||||||
static const String assetsAcFanLow =
|
static const String assetsAcFanLow = "assets/icons/functions_icons/ac_fan_low.svg";
|
||||||
"assets/icons/functions_icons/ac_fan_low.svg";
|
|
||||||
static const String assetsAcFanMiddle =
|
static const String assetsAcFanMiddle =
|
||||||
"assets/icons/functions_icons/ac_fan_middle.svg";
|
"assets/icons/functions_icons/ac_fan_middle.svg";
|
||||||
static const String assetsAcFanHigh =
|
static const String assetsAcFanHigh =
|
||||||
@ -322,8 +307,7 @@ class Assets {
|
|||||||
"assets/icons/functions_icons/far_detection.svg";
|
"assets/icons/functions_icons/far_detection.svg";
|
||||||
static const String assetsFarDetectionFunction =
|
static const String assetsFarDetectionFunction =
|
||||||
"assets/icons/functions_icons/far_detection_function.svg";
|
"assets/icons/functions_icons/far_detection_function.svg";
|
||||||
static const String assetsIndicator =
|
static const String assetsIndicator = "assets/icons/functions_icons/indicator.svg";
|
||||||
"assets/icons/functions_icons/indicator.svg";
|
|
||||||
static const String assetsMotionDetection =
|
static const String assetsMotionDetection =
|
||||||
"assets/icons/functions_icons/motion_detection.svg";
|
"assets/icons/functions_icons/motion_detection.svg";
|
||||||
static const String assetsMotionlessDetection =
|
static const String assetsMotionlessDetection =
|
||||||
@ -336,8 +320,7 @@ class Assets {
|
|||||||
"assets/icons/functions_icons/master_state.svg";
|
"assets/icons/functions_icons/master_state.svg";
|
||||||
static const String assetsSwitchAlarmSound =
|
static const String assetsSwitchAlarmSound =
|
||||||
"assets/icons/functions_icons/switch_alarm_sound.svg";
|
"assets/icons/functions_icons/switch_alarm_sound.svg";
|
||||||
static const String assetsResetOff =
|
static const String assetsResetOff = "assets/icons/functions_icons/reset_off.svg";
|
||||||
"assets/icons/functions_icons/reset_off.svg";
|
|
||||||
|
|
||||||
// Assets for automation_functions
|
// Assets for automation_functions
|
||||||
static const String assetsCardUnlock =
|
static const String assetsCardUnlock =
|
||||||
@ -381,14 +364,12 @@ class Assets {
|
|||||||
static const String activeUser = 'assets/icons/active_user.svg';
|
static const String activeUser = 'assets/icons/active_user.svg';
|
||||||
static const String deActiveUser = 'assets/icons/deactive_user.svg';
|
static const String deActiveUser = 'assets/icons/deactive_user.svg';
|
||||||
static const String invitedIcon = 'assets/icons/invited_icon.svg';
|
static const String invitedIcon = 'assets/icons/invited_icon.svg';
|
||||||
static const String rectangleCheckBox =
|
static const String rectangleCheckBox = 'assets/icons/rectangle_check_box.png';
|
||||||
'assets/icons/rectangle_check_box.png';
|
|
||||||
static const String CheckBoxChecked = 'assets/icons/box_checked.png';
|
static const String CheckBoxChecked = 'assets/icons/box_checked.png';
|
||||||
static const String emptyBox = 'assets/icons/empty_box.png';
|
static const String emptyBox = 'assets/icons/empty_box.png';
|
||||||
static const String completeProcessIcon =
|
static const String completeProcessIcon =
|
||||||
'assets/icons/compleate_process_icon.svg';
|
'assets/icons/compleate_process_icon.svg';
|
||||||
static const String currentProcessIcon =
|
static const String currentProcessIcon = 'assets/icons/current_process_icon.svg';
|
||||||
'assets/icons/current_process_icon.svg';
|
|
||||||
static const String uncomplete_ProcessIcon =
|
static const String uncomplete_ProcessIcon =
|
||||||
'assets/icons/uncompleate_process_icon.svg';
|
'assets/icons/uncompleate_process_icon.svg';
|
||||||
static const String wrongProcessIcon = 'assets/icons/wrong_process_icon.svg';
|
static const String wrongProcessIcon = 'assets/icons/wrong_process_icon.svg';
|
||||||
@ -409,11 +390,9 @@ class Assets {
|
|||||||
static const String successIcon = 'assets/icons/success_icon.svg';
|
static const String successIcon = 'assets/icons/success_icon.svg';
|
||||||
static const String spaceLocationIcon = 'assets/icons/spaseLocationIcon.svg';
|
static const String spaceLocationIcon = 'assets/icons/spaseLocationIcon.svg';
|
||||||
static const String scenesPlayIcon = 'assets/icons/scenesPlayIcon.png';
|
static const String scenesPlayIcon = 'assets/icons/scenesPlayIcon.png';
|
||||||
static const String scenesPlayIconCheck =
|
static const String scenesPlayIconCheck = 'assets/icons/scenesPlayIconCheck.png';
|
||||||
'assets/icons/scenesPlayIconCheck.png';
|
|
||||||
static const String presenceStateIcon = 'assets/icons/presence_state.svg';
|
static const String presenceStateIcon = 'assets/icons/presence_state.svg';
|
||||||
static const String currentDistanceIcon =
|
static const String currentDistanceIcon = 'assets/icons/current_distance_icon.svg';
|
||||||
'assets/icons/current_distance_icon.svg';
|
|
||||||
|
|
||||||
static const String farDetectionIcon = 'assets/icons/far_detection_icon.svg';
|
static const String farDetectionIcon = 'assets/icons/far_detection_icon.svg';
|
||||||
static const String motionDetectionSensitivityIcon =
|
static const String motionDetectionSensitivityIcon =
|
||||||
@ -423,11 +402,12 @@ class Assets {
|
|||||||
'assets/icons/motionless_detection_sensitivity_icon.svg';
|
'assets/icons/motionless_detection_sensitivity_icon.svg';
|
||||||
|
|
||||||
static const String IndicatorIcon = 'assets/icons/Indicator_icon.svg';
|
static const String IndicatorIcon = 'assets/icons/Indicator_icon.svg';
|
||||||
static const String motionDetectionSensitivityValueIcon = 'assets/icons/motion_detection_sensitivity_value_icon.svg';
|
static const String motionDetectionSensitivityValueIcon =
|
||||||
|
'assets/icons/motion_detection_sensitivity_value_icon.svg';
|
||||||
static const String presenceTimeIcon = 'assets/icons/presence_time_icon.svg';
|
static const String presenceTimeIcon = 'assets/icons/presence_time_icon.svg';
|
||||||
static const String IlluminanceIcon = 'assets/icons/Illuminance_icon.svg';
|
static const String IlluminanceIcon = 'assets/icons/Illuminance_icon.svg';
|
||||||
static const String gear = 'assets/icons/gear.svg';
|
static const String gear = 'assets/icons/gear.svg';
|
||||||
static const String activeBell='assets/icons/active_bell.svg';
|
static const String activeBell = 'assets/icons/active_bell.svg';
|
||||||
static const String cpsCustomMode = 'assets/icons/cps_custom_mode.svg';
|
static const String cpsCustomMode = 'assets/icons/cps_custom_mode.svg';
|
||||||
static const String cpsMode1 = 'assets/icons/cps_mode1.svg';
|
static const String cpsMode1 = 'assets/icons/cps_mode1.svg';
|
||||||
static const String cpsMode2 = 'assets/icons/cps_mode2.svg';
|
static const String cpsMode2 = 'assets/icons/cps_mode2.svg';
|
||||||
@ -445,7 +425,8 @@ class Assets {
|
|||||||
static const String motionMeter = 'assets/icons/motion_meter.svg';
|
static const String motionMeter = 'assets/icons/motion_meter.svg';
|
||||||
static const String spatialStaticValue = 'assets/icons/spatial_static_value.svg';
|
static const String spatialStaticValue = 'assets/icons/spatial_static_value.svg';
|
||||||
static const String spatialMotionValue = 'assets/icons/spatial_motion_value.svg';
|
static const String spatialMotionValue = 'assets/icons/spatial_motion_value.svg';
|
||||||
static const String presenceJudgementThrshold = 'assets/icons/presence_judgement_threshold.svg';
|
static const String presenceJudgementThrshold =
|
||||||
|
'assets/icons/presence_judgement_threshold.svg';
|
||||||
static const String spaceType = 'assets/icons/space_type.svg';
|
static const String spaceType = 'assets/icons/space_type.svg';
|
||||||
static const String sportsPara = 'assets/icons/sports_para.svg';
|
static const String sportsPara = 'assets/icons/sports_para.svg';
|
||||||
static const String sensitivityFeature1 = 'assets/icons/sensitivity_feature_1.svg';
|
static const String sensitivityFeature1 = 'assets/icons/sensitivity_feature_1.svg';
|
||||||
@ -457,4 +438,5 @@ class Assets {
|
|||||||
static const String sensitivityFeature7 = 'assets/icons/sensitivity_feature_7.svg';
|
static const String sensitivityFeature7 = 'assets/icons/sensitivity_feature_7.svg';
|
||||||
static const String sensitivityFeature8 = 'assets/icons/sensitivity_feature_8.svg';
|
static const String sensitivityFeature8 = 'assets/icons/sensitivity_feature_8.svg';
|
||||||
static const String sensitivityFeature9 = 'assets/icons/sensitivity_feature_9.svg';
|
static const String sensitivityFeature9 = 'assets/icons/sensitivity_feature_9.svg';
|
||||||
|
static const String deviceTagIcon = 'assets/icons/device_tag_ic.svg';
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ dependencies:
|
|||||||
firebase_core: ^3.11.0
|
firebase_core: ^3.11.0
|
||||||
firebase_crashlytics: ^4.3.2
|
firebase_crashlytics: ^4.3.2
|
||||||
firebase_database: ^11.3.2
|
firebase_database: ^11.3.2
|
||||||
|
bloc: ^8.1.4
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
Reference in New Issue
Block a user