Compare commits

..

4 Commits

Author SHA1 Message Date
09fb604acc added filtering 2025-04-25 10:49:25 +04:00
fbc45b465f Merge pull request #153 from SyncrowIOT/SP-1344-FE-Real-Time-Issues-Ceiling-Sensor-AC-and-Garage-Door-Sensor
Refactor widget lifecycle methods for temperature control and presenc…
2025-04-23 16:29:36 +03:00
c9c939c59f Merge pull request #154 from SyncrowIOT/Add-Flush-Mounted-Presence-Sensor-Single-Control
Add flush mounted presence sensor single control
2025-04-23 16:21:17 +03:00
d1bb7b129f Refactor widget lifecycle methods for temperature control and presence sensor 2025-04-23 10:46:56 +03:00
6 changed files with 119 additions and 80 deletions

View File

@ -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
void dispose() {
_debounce?.cancel();

View File

@ -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/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});
final AllDevicesModel device;
@ -31,29 +32,35 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
..add(CeilingInitialEvent(device.uuid ?? '')),
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
builder: (context, state) {
if (state is CeilingLoadingInitialState || state is CeilingReportsLoadingState) {
if (state is CeilingLoadingInitialState ||
state is CeilingReportsLoadingState) {
return const Center(child: CircularProgressIndicator());
} else if (state is CeilingUpdateState) {
return _buildGridView(
context, state.ceilingSensorModel, isExtraLarge, isLarge, isMedium);
return _buildGridView(context, state.ceilingSensorModel,
isExtraLarge, isLarge, isMedium);
} else if (state is CeilingReportsState) {
return ReportsTable(
report: state.deviceReport,
onRowTap: (index) {},
onClose: () {
context.read<CeilingSensorBloc>().add(BackToCeilingGridViewEvent());
context
.read<CeilingSensorBloc>()
.add(BackToCeilingGridViewEvent());
},
);
} else if (state is ShowCeilingDescriptionState) {
return DescriptionView(
description: state.description,
onClose: () {
context.read<CeilingSensorBloc>().add(BackToCeilingGridViewEvent());
context
.read<CeilingSensorBloc>()
.add(BackToCeilingGridViewEvent());
},
);
} else if (state is CeilingReportsFailedState) {
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'));
},
@ -61,8 +68,8 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
);
}
Widget _buildGridView(BuildContext context, CeilingSensorModel model, bool isExtraLarge,
bool isLarge, bool isMedium) {
Widget _buildGridView(BuildContext context, CeilingSensorModel model,
bool isExtraLarge, bool isLarge, bool isMedium) {
return GridView(
padding: const EdgeInsets.symmetric(horizontal: 50),
shrinkWrap: true,
@ -143,8 +150,8 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
),
GestureDetector(
onTap: () {
context.read<CeilingSensorBloc>().add(
GetCeilingDeviceReportsEvent(code: 'presence_state', deviceUuid: device.uuid!));
context.read<CeilingSensorBloc>().add(GetCeilingDeviceReportsEvent(
code: 'presence_state', deviceUuid: device.uuid!));
},
child: const PresenceStaticWidget(
icon: Assets.illuminanceRecordIcon,
@ -153,9 +160,8 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
),
GestureDetector(
onTap: () {
context
.read<CeilingSensorBloc>()
.add(GetCeilingDeviceReportsEvent(code: '', deviceUuid: device.uuid!));
context.read<CeilingSensorBloc>().add(GetCeilingDeviceReportsEvent(
code: '', deviceUuid: device.uuid!));
},
child: const PresenceStaticWidget(
icon: Assets.helpDescriptionIcon,

View File

@ -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
Widget build(BuildContext context) {
return DeviceControlsContainer(

View File

@ -21,6 +21,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
on<ShowDescriptionEvent>(_showDescription);
on<BackToGridViewEvent>(_backToGridView);
on<WallSensorFactoryResetEvent>(_onFactoryReset);
on<WallSensorRealtimeUpdateEvent>(_onRealtimeUpdate);
}
void _fetchWallSensorStatus(
@ -30,7 +31,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
var response = await DevicesManagementApi().getDeviceStatus(deviceId);
deviceStatus = WallSensorModel.fromJson(response.status);
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
_listenToChanges(emit, deviceId);
_listenToChanges(deviceId);
} catch (e) {
emit(WallSensorFailedState(error: e.toString()));
return;
@ -52,28 +53,27 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
}
}
_listenToChanges(Emitter<WallSensorState> emit, deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
void _listenToChanges(String deviceId) {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
ref.onValue.listen((DatabaseEvent event) {
final data = event.snapshot.value as Map<dynamic, dynamic>?;
if (data == null) return;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
final statusList = (data['status'] as List?)
?.map((e) => Status(code: e['code'], value: e['value']))
.toList();
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus = WallSensorModel.fromJson(statusList);
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
});
} catch (_) {}
if (statusList != null) {
final updatedDeviceStatus = WallSensorModel.fromJson(statusList);
if (!isClosed) {
add(WallSensorRealtimeUpdateEvent(updatedDeviceStatus));
}
}
});
}
void _changeValue(
WallSensorChangeValueEvent event, Emitter<WallSensorState> emit) async {
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
@ -195,4 +195,12 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
emit(WallSensorFailedState(error: e.toString()));
}
}
void _onRealtimeUpdate(
WallSensorRealtimeUpdateEvent event,
Emitter<WallSensorState> emit,
) {
deviceStatus = event.deviceStatus;
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
}
}

View File

@ -1,5 +1,6 @@
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/wall_sensor/model/wall_sensor_model.dart';
abstract class WallSensorEvent extends Equatable {
const WallSensorEvent();
@ -70,3 +71,8 @@ class WallSensorFactoryResetEvent extends WallSensorEvent {
required this.factoryReset,
});
}
class WallSensorRealtimeUpdateEvent extends WallSensorEvent {
final WallSensorModel deviceStatus;
const WallSensorRealtimeUpdateEvent(this.deviceStatus);
}

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 = '');