Compare commits

..

1 Commits

Author SHA1 Message Date
09fb604acc added filtering 2025-04-25 10:49:25 +04:00
9 changed files with 81 additions and 115 deletions

View File

@ -9,7 +9,6 @@ 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/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/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_control_view.dart';
@ -199,10 +198,6 @@ mixin RouteControlsBasedCode {
return SOSBatchControlView(
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:
return const SizedBox();
}

View File

@ -49,9 +49,6 @@ class FlushMountedPresenceSensorBloc
on<FlushMountedPresenceSensorFactoryResetEvent>(
_onFlushMountedPresenceSensorFactoryResetEvent,
);
on<FlushMountedPresenceSensorStatusUpdatedEvent>(
_onFlushMountedPresenceSensorStatusUpdatedEvent,
);
}
void _onFlushMountedPresenceSensorFetchStatusEvent(
@ -63,7 +60,7 @@ class FlushMountedPresenceSensorBloc
final response = await DevicesManagementApi().getDeviceStatus(deviceId);
deviceStatus = FlushMountedPresenceSensorModel.fromJson(response.status);
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
_listenToChanges(deviceId);
_listenToChanges(emit, deviceId);
} catch (e) {
emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
return;
@ -84,33 +81,31 @@ class FlushMountedPresenceSensorBloc
}
}
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>;
Future<void> _listenToChanges(
Emitter<FlushMountedPresenceSensorState> emit,
String deviceId,
) async {
final ref = FirebaseDatabase.instance.ref(
'device-status/$deviceId',
);
await ref.onValue.listen(
(DatabaseEvent event) async {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
eventsMap['status'].forEach((element) {
statusList.add(
Status(code: element['code'], value: element['value']),
);
(usersMap['status'] as List<dynamic>?)?.forEach((element) {
statusList.add(Status(code: element['code'], value: element['value']));
});
deviceStatus = FlushMountedPresenceSensorModel.fromJson(statusList);
if (!isClosed) {
add(FlushMountedPresenceSensorStatusUpdatedEvent(deviceStatus));
if (!emit.isDone) {
emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus));
}
});
} catch (_) {
log(
'Error listening to changes',
name: 'FlushMountedPresenceSensorBloc._listenToChanges',
);
}
},
onError: (error) => log(error.toString(), name: 'FirebaseDatabaseError'),
).asFuture();
}
void _onFlushMountedPresenceSensorChangeValueEvent(
@ -239,12 +234,4 @@ class FlushMountedPresenceSensorBloc
emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
}
}
void _onFlushMountedPresenceSensorStatusUpdatedEvent(
FlushMountedPresenceSensorStatusUpdatedEvent event,
Emitter<FlushMountedPresenceSensorState> emit,
) {
deviceStatus = event.model;
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
}
}

View File

@ -10,16 +10,6 @@ sealed class FlushMountedPresenceSensorEvent extends Equatable {
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;

View File

@ -16,6 +16,6 @@ abstract final class FlushMountedPresenceSensorBlocFactory {
batchControlDevicesService: DebouncedBatchControlDevicesService(
decoratee: RemoteBatchControlDevicesService(),
),
);
)..add(FlushMountedPresenceSensorFetchStatusEvent());
}
}

View File

@ -22,7 +22,7 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
return BlocProvider(
create: (context) => FlushMountedPresenceSensorBlocFactory.create(
deviceId: devicesIds.first,
)..add(FlushMountedPresenceSensorFetchBatchStatusEvent(devicesIds)),
),
child: BlocBuilder<FlushMountedPresenceSensorBloc,
FlushMountedPresenceSensorState>(
builder: (context, state) {
@ -67,8 +67,7 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
maxValue: 9,
steps: 1,
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorBatchControlEvent(
deviceIds: devicesIds,
FlushMountedPresenceSensorChangeValueEvent(
code: FlushMountedPresenceSensorModel.codeSensitivity,
value: value,
),
@ -84,8 +83,7 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
valuesPercision: 1,
action: (double value) =>
context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorBatchControlEvent(
deviceIds: devicesIds,
FlushMountedPresenceSensorChangeValueEvent(
code: FlushMountedPresenceSensorModel.codeNearDetection,
value: (value * 100).toInt(),
),
@ -101,8 +99,7 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
valuesPercision: 1,
action: (double value) =>
context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorBatchControlEvent(
deviceIds: devicesIds,
FlushMountedPresenceSensorChangeValueEvent(
code: FlushMountedPresenceSensorModel.codeFarDetection,
value: (value * 100).toInt(),
),
@ -115,22 +112,20 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
maxValue: 3,
steps: 1,
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorBatchControlEvent(
deviceIds: devicesIds,
FlushMountedPresenceSensorChangeValueEvent(
code: FlushMountedPresenceSensorModel.codePresenceDelay,
value: value,
),
),
),
PresenceUpdateData(
value: model.occurDistReduce.toDouble(),
value: (model.occurDistReduce.toDouble()),
title: 'Indent Level:',
minValue: 0,
maxValue: 3,
steps: 1,
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorBatchControlEvent(
deviceIds: devicesIds,
FlushMountedPresenceSensorChangeValueEvent(
code: FlushMountedPresenceSensorModel.codeOccurDistReduce,
value: value,
),
@ -144,8 +139,7 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
maxValue: 3,
steps: 1,
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorBatchControlEvent(
deviceIds: devicesIds,
FlushMountedPresenceSensorChangeValueEvent(
code: FlushMountedPresenceSensorModel.codeSensiReduce,
value: value,
),
@ -160,8 +154,7 @@ class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
steps: 1,
action: (double value) =>
context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorBatchControlEvent(
deviceIds: devicesIds,
FlushMountedPresenceSensorChangeValueEvent(
code: FlushMountedPresenceSensorModel.codeNoneDelay,
value: (value * 10).round(),
),

View File

@ -24,7 +24,7 @@ class FlushMountedPresenceSensorControlView extends StatelessWidget
return BlocProvider(
create: (context) => FlushMountedPresenceSensorBlocFactory.create(
deviceId: device.uuid ?? '-1',
)..add(FlushMountedPresenceSensorFetchStatusEvent()),
),
child: BlocBuilder<FlushMountedPresenceSensorBloc,
FlushMountedPresenceSensorState>(
builder: (context, state) {

View File

@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.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/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_event.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
@ -15,6 +16,8 @@ import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/cent
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_event.dart';
import 'package:syncrow_web/utils/style.dart';
import '../../../space_tree/bloc/space_tree_event.dart';
class SidebarWidget extends StatefulWidget {
final List<CommunityModel> communities;
final String? selectedSpaceUuid;
@ -40,13 +43,31 @@ class _SidebarWidgetState extends State<SidebarWidget> {
@override
void initState() {
_selectedId = widget.selectedSpaceUuid;
_scrollController = ScrollController();
_scrollController.addListener(_onScroll);
_selectedId = widget.selectedSpaceUuid;
super.initState();
}
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();
}
@ -59,38 +80,6 @@ class _SidebarWidgetState extends State<SidebarWidget> {
super.didUpdateWidget(oldWidget);
}
List<CommunityModel> _filteredCommunities() {
if (_searchQuery.isEmpty) {
_selectedSpaceUuid = null;
return widget.communities;
}
return widget.communities.where((community) {
final containsQueryInCommunity =
community.name.toLowerCase().contains(_searchQuery.toLowerCase());
final containsQueryInSpaces = community.spaces.any((space) =>
_containsQuery(space: space, query: _searchQuery.toLowerCase()));
return containsQueryInCommunity || containsQueryInSpaces;
}).toList();
}
bool _containsQuery({
required SpaceModel space,
required String query,
}) {
final matchesSpace = space.name.toLowerCase().contains(query);
final matchesChildren = space.children.any(
(child) => _containsQuery(space: child, query: query),
);
if (matchesSpace || matchesChildren) {
_selectedSpaceUuid = space.uuid;
}
return matchesSpace || matchesChildren;
}
bool _isSpaceOrChildSelected(SpaceModel space) {
final isSpaceSelected = _selectedSpaceUuid == space.uuid;
final anySubSpaceIsSelected = space.children.any(_isSpaceOrChildSelected);
@ -101,7 +90,10 @@ class _SidebarWidgetState extends State<SidebarWidget> {
@override
Widget build(BuildContext context) {
final filteredCommunities = _filteredCommunities();
final spaceTreeState = context.watch<SpaceTreeBloc>().state;
final filteredCommunities = spaceTreeState.isSearching
? spaceTreeState.filteredCommunity
: spaceTreeState.communityList;
return Container(
width: _width,
@ -112,7 +104,13 @@ class _SidebarWidgetState extends State<SidebarWidget> {
children: [
SidebarHeader(onAddCommunity: _onAddCommunity),
CustomSearchBar(
onSearchChanged: (query) => setState(() => _searchQuery = query),
onSearchChanged: (query) {
setState(() {
_searchQuery = query;
});
context.read<SpaceTreeBloc>().add(SearchQueryEvent(query));
},
),
const SizedBox(height: 16),
Expanded(
@ -120,14 +118,18 @@ class _SidebarWidgetState extends State<SidebarWidget> {
visible: filteredCommunities.isNotEmpty,
replacement: const EmptySearchResultWidget(),
child: SidebarCommunitiesList(
scrollController: _scrollController,
onScrollToEnd: () {},
communities: filteredCommunities,
itemBuilder: (context, index) => _buildCommunityTile(
context,
filteredCommunities[index],
),
),
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]);
}),
),
),
],
@ -205,9 +207,8 @@ class _SidebarWidgetState extends State<SidebarWidget> {
);
}
void _onAddCommunity() => _selectedId?.isNotEmpty ?? true
? _clearSelection()
: _showCreateCommunityDialog();
void _onAddCommunity() =>
_selectedId?.isNotEmpty ?? true ? _clearSelection() : _showCreateCommunityDialog();
void _clearSelection() {
setState(() => _selectedId = '');

View File

@ -51,7 +51,7 @@ final class DebouncedBatchControlDevicesService
DebouncedBatchControlDevicesService({
required this.decoratee,
this.debounceDuration = const Duration(milliseconds: 800),
this.debounceDuration = const Duration(milliseconds: 1500),
});
@override

View File

@ -40,7 +40,7 @@ final class DebouncedControlDeviceService implements ControlDeviceService {
DebouncedControlDeviceService({
required this.decoratee,
this.debounceDuration = const Duration(milliseconds: 800),
this.debounceDuration = const Duration(milliseconds: 1500),
});
final _pendingRequests = <(String deviceUuid, Status status)>[];