Compare commits

..

1 Commits

Author SHA1 Message Date
f7241ca689 uncommented code. 2025-04-09 10:41:24 +03:00
31 changed files with 690 additions and 1174 deletions

View File

@ -201,17 +201,20 @@ class ForgetPasswordWebPage extends StatelessWidget {
!state.isButtonEnabled && !state.isButtonEnabled &&
state.remainingTime != 1 state.remainingTime != 1
? null ? null
: : () {
() {
if (forgetBloc if (forgetBloc
.forgetEmailKey .forgetEmailKey.currentState!
.currentState! .validate() ||
.validate()) { forgetBloc
forgetBloc.add( .forgetRegionKey.currentState!
StartTimerEvent()); .validate()) {
if (forgetBloc
.forgetRegionKey.currentState!
.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(

View File

@ -13,7 +13,6 @@ 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);
@ -22,16 +21,7 @@ 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 {
@ -40,23 +30,8 @@ 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()));
} }
@ -95,16 +70,31 @@ 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(status: deviceStatus)); emit(ACStatusLoaded(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(status: deviceStatus)); emit(ACStatusLoaded(deviceStatus));
await _runDebounce( await _runDebounce(
isBatch: false, isBatch: false,
@ -161,7 +151,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(status: deviceStatus)); emit(ACStatusLoaded(deviceStatus));
} }
void _updateLocalValue(String code, dynamic value, Emitter<AcsState> emit) { void _updateLocalValue(String code, dynamic value, Emitter<AcsState> emit) {
@ -194,16 +184,11 @@ 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(status: deviceStatus)); emit(ACStatusLoaded(deviceStatus));
} }
dynamic _getValueByCode(String code) { dynamic _getValueByCode(String code) {
@ -218,8 +203,6 @@ 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;
} }
@ -233,7 +216,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(status: deviceStatus)); emit(ACStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(AcsFailedState(error: e.toString())); emit(AcsFailedState(error: e.toString()));
} }
@ -245,7 +228,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
_updateLocalValue(event.code, event.value, emit); _updateLocalValue(event.code, event.value, emit);
emit(ACStatusLoaded(status: deviceStatus)); emit(ACStatusLoaded(deviceStatus));
await _runDebounce( await _runDebounce(
isBatch: true, isBatch: true,
@ -274,144 +257,4 @@ 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();
}
} }

View File

@ -8,7 +8,6 @@ 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 {
@ -19,12 +18,10 @@ 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;
@ -76,30 +73,3 @@ 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);
}

View File

@ -2,9 +2,8 @@ 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 {
final bool isTimerActive; const AcsState();
const AcsState({this.isTimerActive = false});
@override @override
List<Object> get props => []; List<Object> get props => [];
} }
@ -16,30 +15,8 @@ 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({ ACStatusLoaded(this.status) : timestamp = DateTime.now();
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];
@ -63,14 +40,3 @@ 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];
}

View File

@ -11,7 +11,6 @@ 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,
@ -19,7 +18,6 @@ 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),
@ -32,7 +30,6 @@ 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) {
@ -54,9 +51,6 @@ 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;
} }
} }
@ -68,7 +62,6 @@ class AcStatusModel {
currentTemp: currentTemp, currentTemp: currentTemp,
fanSpeedsString: fanSpeeds, fanSpeedsString: fanSpeeds,
childLock: childLock, childLock: childLock,
countdown1: _countdown1,
); );
} }
@ -80,7 +73,6 @@ 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,
@ -90,7 +82,6 @@ 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,
); );
} }

View File

@ -10,10 +10,11 @@ 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 {
const AcDeviceControlsView({super.key, required this.device}); const AcDeviceControlsView({super.key, required this.device});
final AllDevicesModel device; final AllDevicesModel device;
@ -22,13 +23,11 @@ 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),
@ -79,101 +78,56 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
), ),
ToggleWidget( ToggleWidget(
label: '', label: '',
labelWidget: Column( labelWidget: Row(
crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Container( IconButton(
width: MediaQuery.of(context).size.width, padding: const EdgeInsets.all(0),
decoration: const ShapeDecoration( onPressed: () {},
color: ColorsManager.primaryColor, icon: const Icon(
shape: RoundedRectangleBorder( Icons.remove,
borderRadius: BorderRadius.all(Radius.circular(30)), size: 28,
), color: ColorsManager.greyColor,
), ),
), ),
Center( Text(
child: SizedBox( '06',
child: Row( style: context.textTheme.titleLarge!.copyWith(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, color: ColorsManager.dialogBlueTitle,
children: [ fontWeight: FontWeight.bold,
IconButton( ),
onPressed: () { ),
if (acBloc.timerActive == false) { Text(
context 'h',
.read<AcBloc>() style: context.textTheme.bodySmall!
.add(DecreaseTimeEvent()); .copyWith(color: ColorsManager.blackColor),
} ),
}, Text(
icon: const Icon(Icons.remove, '30',
color: ColorsManager.greyColor), style: context.textTheme.titleLarge!.copyWith(
), color: ColorsManager.dialogBlueTitle,
Text( fontWeight: FontWeight.bold,
acBloc.scheduledHours ),
.toString() ),
.padLeft(2, '0'), Text('m',
style: Theme.of(context) style: context.textTheme.bodySmall!
.textTheme .copyWith(color: ColorsManager.blackColor)),
.titleLarge! IconButton(
.copyWith( padding: const EdgeInsets.all(0),
color: ColorsManager.dialogBlueTitle, onPressed: () {},
fontWeight: FontWeight.bold, icon: const Icon(
), Icons.add,
), size: 28,
Text( color: ColorsManager.greyColor,
'h',
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: ColorsManager.blackColor,
),
),
Text(
acBloc.scheduledMinutes
.toString()
.padLeft(2, '0'),
style: Theme.of(context)
.textTheme
.titleLarge!
.copyWith(
color: ColorsManager.dialogBlueTitle,
fontWeight: FontWeight.bold,
),
),
Text(
'm',
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: ColorsManager.blackColor,
),
),
IconButton(
onPressed: () {
if (acBloc.timerActive == false) {
context
.read<AcBloc>()
.add(IncreaseTimeEvent());
}
},
icon: const Icon(Icons.add,
color: ColorsManager.greyColor),
),
],
),
), ),
), ),
], ],
), ),
value: acBloc.timerActive, value: false,
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!,

View File

@ -248,18 +248,12 @@ SOS
switch (productType) { switch (productType) {
case 'AC': case 'AC':
return [ return [
SwitchFunction( SwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), ModeFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
ModeFunction( TempSetFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), CurrentTempFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
TempSetFunction( LevelFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), ChildLockFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
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':
@ -282,17 +276,17 @@ SOS
case '3G': case '3G':
return [ return [
ThreeGangSwitch1Function( ThreeGangSwitch1Function(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangSwitch2Function( ThreeGangSwitch2Function(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangSwitch3Function( ThreeGangSwitch3Function(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangCountdown1Function( ThreeGangCountdown1Function(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangCountdown2Function( ThreeGangCountdown2Function(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangCountdown3Function( ThreeGangCountdown3Function(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'), deviceId: uuid ?? '', deviceName: name ?? ''),
]; ];
case 'WPS': case 'WPS':
return [ return [
@ -318,6 +312,8 @@ SOS
NoOneTimeFunction( NoOneTimeFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'), deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
// FarDetectionSliderFunction(
// deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN')
]; ];
case 'GW': case 'GW':
return [ return [

View File

@ -14,36 +14,27 @@ class GatewayModel {
}); });
factory GatewayModel.fromJson(Map<String, dynamic> json) { factory GatewayModel.fromJson(Map<String, dynamic> json) {
final status = json['status'] as List<dynamic>? ?? <dynamic>[]; final status = json['status'] as List<dynamic>;
bool? switchAlarmSound; final switchAlarmSound = status.firstWhere(
String? masterState; (item) => item['code'] == 'switch_alarm_sound',
bool? factoryReset; )['value'] as bool;
String? alarmActive; final masterState = status.firstWhere(
(item) => item['code'] == 'master_state',
for (final item in status) { )['value'] as String;
switch (item['code']) { final factoryReset = status.firstWhere(
case 'switch_alarm_sound': (item) => item['code'] == 'factory_reset',
switchAlarmSound = item['value'] as bool; )['value'] as bool;
break; final alarmActive = status.firstWhere(
case 'master_state': (item) => item['code'] == 'alarm_active',
masterState = item['value'] as String; )['value'] as String;
break;
case 'factory_reset':
factoryReset = item['value'] as bool;
break;
case 'alarm_active':
alarmActive = item['value'] as String;
break;
}
}
return GatewayModel( return GatewayModel(
uuid: json['uuid'] as String? ?? '', uuid: json['uuid'] as String,
switchAlarmSound: switchAlarmSound ?? false, switchAlarmSound: switchAlarmSound,
masterState: masterState ?? '', masterState: masterState,
factoryReset: factoryReset ?? false, factoryReset: factoryReset,
alarmActive: alarmActive ?? '', alarmActive: alarmActive,
); );
} }
} }

View File

@ -62,6 +62,9 @@ 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,

View File

@ -13,8 +13,7 @@ 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 class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
with HelperResponsiveLayout {
const SosDeviceControlsView({ const SosDeviceControlsView({
super.key, super.key,
required this.device, required this.device,
@ -25,8 +24,7 @@ class SosDeviceControlsView extends StatelessWidget
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => create: (context) => SosDeviceBloc()..add(GetDeviceStatus(device.uuid!)),
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) {
@ -65,8 +63,7 @@ class SosDeviceControlsView extends StatelessWidget
)); ));
} }
Widget _buildStatusControls( Widget _buildStatusControls(BuildContext context, SosStatusModel deviceStatus) {
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);
@ -88,7 +85,7 @@ class SosDeviceControlsView extends StatelessWidget
IconNameStatusContainer( IconNameStatusContainer(
isFullIcon: false, isFullIcon: false,
name: deviceStatus.sosStatus == 'sos' ? 'SOS' : 'Normal', name: deviceStatus.sosStatus == 'sos' ? 'SOS' : 'Normal',
icon: deviceStatus.sosStatus == 'sos' ? Assets.sosNormal : Assets.sos, icon: deviceStatus.sosStatus == 'sos' ? Assets.sos : Assets.sosNormal,
onTap: () {}, onTap: () {},
status: false, status: false,
textColor: ColorsManager.blackColor, textColor: ColorsManager.blackColor,

View File

@ -3,7 +3,7 @@ import 'package:flutter_bloc/flutter_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/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ac_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ac_dialog.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/gateway/gateway_helper.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/gateway/gateway_dialog.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/one_gang_switch_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/one_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/two_gang_switch_dialog.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/two_gang_switch_dialog.dart';
@ -50,45 +50,46 @@ class DeviceDialogHelper {
final deviceSelectedFunctions = final deviceSelectedFunctions =
routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? []; routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? [];
if (removeComparetors && data['productType'] != 'WPS') {
//remove the current temp function in the 'if container'
functions.removeAt(3);
}
switch (productType) { switch (productType) {
case 'AC': case 'AC':
return ACHelper.showACFunctionsDialog( return ACHelper.showACFunctionsDialog(
context: context, context,
functions: functions, functions,
device: data['device'], data['device'],
deviceSelectedFunctions: deviceSelectedFunctions, deviceSelectedFunctions,
uniqueCustomId: data['uniqueCustomId'], data['uniqueCustomId'],
removeComparetors: removeComparetors, removeComparetors,
dialogType: dialogType,
); );
case '1G': case '1G':
return OneGangSwitchHelper.showSwitchFunctionsDialog( return OneGangSwitchHelper.showSwitchFunctionsDialog(
dialogType: dialogType, context,
context: context, functions,
functions: functions, data['device'],
device: data['device'], deviceSelectedFunctions,
deviceSelectedFunctions: deviceSelectedFunctions, data['uniqueCustomId'],
uniqueCustomId: data['uniqueCustomId'], removeComparetors);
removeComparetors: removeComparetors);
case '2G': case '2G':
return TwoGangSwitchHelper.showSwitchFunctionsDialog( return TwoGangSwitchHelper.showSwitchFunctionsDialog(
dialogType: dialogType, context,
context: context, functions,
functions: functions, data['device'],
device: data['device'], deviceSelectedFunctions,
deviceSelectedFunctions: deviceSelectedFunctions, data['uniqueCustomId'],
uniqueCustomId: data['uniqueCustomId'], removeComparetors);
removeComparetors: removeComparetors);
case '3G': case '3G':
return ThreeGangSwitchHelper.showSwitchFunctionsDialog( return ThreeGangSwitchHelper.showSwitchFunctionsDialog(
dialogType: dialogType, context,
context: context, functions,
functions: functions, data['device'],
device: data['device'], deviceSelectedFunctions,
deviceSelectedFunctions: deviceSelectedFunctions, data['uniqueCustomId'],
uniqueCustomId: data['uniqueCustomId'], removeComparetors);
removeComparetors: removeComparetors);
case 'WPS': case 'WPS':
return WallPresenceSensor.showWPSFunctionsDialog( return WallPresenceSensor.showWPSFunctionsDialog(
dialogType: dialogType, dialogType: dialogType,
@ -104,7 +105,6 @@ class DeviceDialogHelper {
functions: functions, functions: functions,
uniqueCustomId: data['uniqueCustomId'], uniqueCustomId: data['uniqueCustomId'],
deviceSelectedFunctions: deviceSelectedFunctions, deviceSelectedFunctions: deviceSelectedFunctions,
device: data['device'],
); );
default: default:

View File

@ -5,28 +5,23 @@ import 'package:syncrow_web/utils/constants/app_enum.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
abstract class ACFunction extends DeviceFunction<AcStatusModel> { abstract class ACFunction extends DeviceFunction<AcStatusModel> {
final String type;
ACFunction({ ACFunction({
required super.deviceId, required super.deviceId,
required super.deviceName, required super.deviceName,
required super.code, required super.code,
required super.operationName, required super.operationName,
required super.icon, required super.icon,
required this.type,
}); });
List<ACOperationalValue> getOperationalValues(); List<ACOperationalValue> getOperationalValues();
} }
class SwitchFunction extends ACFunction { class SwitchFunction extends ACFunction {
SwitchFunction( SwitchFunction({required super.deviceId, required super.deviceName})
{required super.deviceId, required super.deviceName, required type})
: super( : super(
code: 'switch', code: 'switch',
operationName: 'Power', operationName: 'Power',
icon: Assets.assetsAcPower, icon: Assets.assetsAcPower,
type: type,
); );
@override @override
@ -45,13 +40,11 @@ class SwitchFunction extends ACFunction {
} }
class ModeFunction extends ACFunction { class ModeFunction extends ACFunction {
ModeFunction( ModeFunction({required super.deviceId, required super.deviceName})
{required super.deviceId, required super.deviceName, required type})
: super( : super(
code: 'mode', code: 'mode',
operationName: 'Mode', operationName: 'Mode',
icon: Assets.assetsFreezing, icon: Assets.assetsFreezing,
type: type,
); );
@override @override
@ -79,8 +72,7 @@ class TempSetFunction extends ACFunction {
final int max; final int max;
final int step; final int step;
TempSetFunction( TempSetFunction({required super.deviceId, required super.deviceName})
{required super.deviceId, required super.deviceName, required type})
: min = 160, : min = 160,
max = 300, max = 300,
step = 1, step = 1,
@ -88,7 +80,6 @@ class TempSetFunction extends ACFunction {
code: 'temp_set', code: 'temp_set',
operationName: 'Set Temperature', operationName: 'Set Temperature',
icon: Assets.assetsTempreture, icon: Assets.assetsTempreture,
type: type,
); );
@override @override
@ -106,10 +97,8 @@ class TempSetFunction extends ACFunction {
} }
class LevelFunction extends ACFunction { class LevelFunction extends ACFunction {
LevelFunction( LevelFunction({required super.deviceId, required super.deviceName})
{required super.deviceId, required super.deviceName, required type})
: super( : super(
type: type,
code: 'level', code: 'level',
operationName: 'Fan Speed', operationName: 'Fan Speed',
icon: Assets.assetsFanSpeed, icon: Assets.assetsFanSpeed,
@ -141,10 +130,8 @@ class LevelFunction extends ACFunction {
} }
class ChildLockFunction extends ACFunction { class ChildLockFunction extends ACFunction {
ChildLockFunction( ChildLockFunction({required super.deviceId, required super.deviceName})
{required super.deviceId, required super.deviceName, required type})
: super( : super(
type: type,
code: 'child_lock', code: 'child_lock',
operationName: 'Child Lock', operationName: 'Child Lock',
icon: Assets.assetsChildLock, icon: Assets.assetsChildLock,
@ -170,13 +157,11 @@ class CurrentTempFunction extends ACFunction {
final int max; final int max;
final int step; final int step;
CurrentTempFunction( CurrentTempFunction({required super.deviceId, required super.deviceName})
{required super.deviceId, required super.deviceName, required type})
: min = -100, : min = -100,
max = 990, max = 990,
step = 1, step = 1,
super( super(
type: type,
code: 'temp_current', code: 'temp_current',
operationName: 'Current Temperature', operationName: 'Current Temperature',
icon: Assets.currentTemp, icon: Assets.currentTemp,

View File

@ -3,7 +3,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operation
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class ThreeGangSwitch1Function extends BaseSwitchFunction { class ThreeGangSwitch1Function extends BaseSwitchFunction {
ThreeGangSwitch1Function({required super.deviceId, required super.deviceName ,required type}) ThreeGangSwitch1Function({required super.deviceId, required super.deviceName})
: super( : super(
code: 'switch_1', code: 'switch_1',
operationName: 'Light 1 Switch', operationName: 'Light 1 Switch',
@ -26,7 +26,7 @@ class ThreeGangSwitch1Function extends BaseSwitchFunction {
} }
class ThreeGangCountdown1Function extends BaseSwitchFunction { class ThreeGangCountdown1Function extends BaseSwitchFunction {
ThreeGangCountdown1Function({required super.deviceId, required super.deviceName ,required type}) ThreeGangCountdown1Function({required super.deviceId, required super.deviceName})
: super( : super(
code: 'countdown_1', code: 'countdown_1',
operationName: 'Light 1 Countdown', operationName: 'Light 1 Countdown',
@ -47,7 +47,7 @@ class ThreeGangCountdown1Function extends BaseSwitchFunction {
} }
class ThreeGangSwitch2Function extends BaseSwitchFunction { class ThreeGangSwitch2Function extends BaseSwitchFunction {
ThreeGangSwitch2Function({required super.deviceId, required super.deviceName, required type}) ThreeGangSwitch2Function({required super.deviceId, required super.deviceName})
: super( : super(
code: 'switch_2', code: 'switch_2',
operationName: 'Light 2 Switch', operationName: 'Light 2 Switch',
@ -70,7 +70,7 @@ class ThreeGangSwitch2Function extends BaseSwitchFunction {
} }
class ThreeGangCountdown2Function extends BaseSwitchFunction { class ThreeGangCountdown2Function extends BaseSwitchFunction {
ThreeGangCountdown2Function({required super.deviceId, required super.deviceName ,required type}) ThreeGangCountdown2Function({required super.deviceId, required super.deviceName})
: super( : super(
code: 'countdown_2', code: 'countdown_2',
operationName: 'Light 2 Countdown', operationName: 'Light 2 Countdown',
@ -91,7 +91,7 @@ class ThreeGangCountdown2Function extends BaseSwitchFunction {
} }
class ThreeGangSwitch3Function extends BaseSwitchFunction { class ThreeGangSwitch3Function extends BaseSwitchFunction {
ThreeGangSwitch3Function({required super.deviceId, required super.deviceName ,required type}) ThreeGangSwitch3Function({required super.deviceId, required super.deviceName})
: super( : super(
code: 'switch_3', code: 'switch_3',
operationName: 'Light 3 Switch', operationName: 'Light 3 Switch',
@ -114,7 +114,7 @@ class ThreeGangSwitch3Function extends BaseSwitchFunction {
} }
class ThreeGangCountdown3Function extends BaseSwitchFunction { class ThreeGangCountdown3Function extends BaseSwitchFunction {
ThreeGangCountdown3Function({required super.deviceId, required super.deviceName ,required type}) ThreeGangCountdown3Function({required super.deviceId, required super.deviceName})
: super( : super(
code: 'countdown_3', code: 'countdown_3',
operationName: 'Light 3 Countdown', operationName: 'Light 3 Countdown',

View File

@ -33,11 +33,10 @@ final class GatewaySwitchAlarmSound extends GatewayFunctions {
required super.deviceId, required super.deviceId,
required super.deviceName, required super.deviceName,
required super.type, required super.type,
}) : super( super.code = 'switch_alarm_sound',
code: 'switch_alarm_sound', super.operationName = 'Switch Alarm Sound',
operationName: 'Switch Alarm Sound', super.icon = Assets.activeBell,
icon: Assets.activeBell, });
);
@override @override
List<GatewayOperationalValue> getOperationalValues() => [ List<GatewayOperationalValue> getOperationalValues() => [
@ -59,11 +58,10 @@ final class GatewayMasterState extends GatewayFunctions {
required super.deviceId, required super.deviceId,
required super.deviceName, required super.deviceName,
required super.type, required super.type,
}) : super( super.code = 'master_state',
code: 'master_state', super.operationName = 'Master State',
operationName: 'Master State', super.icon = Assets.gear,
icon: Assets.gear, });
);
@override @override
List<GatewayOperationalValue> getOperationalValues() { List<GatewayOperationalValue> getOperationalValues() {
@ -71,12 +69,12 @@ final class GatewayMasterState extends GatewayFunctions {
GatewayOperationalValue( GatewayOperationalValue(
icon: Assets.assetsAcPower, icon: Assets.assetsAcPower,
description: "Normal", description: "Normal",
value: 'normal', value: 'Normal',
), ),
GatewayOperationalValue( GatewayOperationalValue(
icon: Assets.assetsAcPowerOFF, icon: Assets.assetsAcPowerOFF,
description: "Alarm", description: "Alarm",
value: 'alarm', value: 'Alarm',
), ),
]; ];
} }
@ -87,11 +85,10 @@ final class GatewayFactoryReset extends GatewayFunctions {
required super.deviceId, required super.deviceId,
required super.deviceName, required super.deviceName,
required super.type, required super.type,
}) : super( super.code = 'factory_reset',
code: 'factory_reset', super.operationName = 'Factory Reset',
operationName: 'Factory Reset', super.icon = Assets.factoryReset,
icon: Assets.factoryReset, });
);
@override @override
List<GatewayOperationalValue> getOperationalValues() { List<GatewayOperationalValue> getOperationalValues() {

View File

@ -183,7 +183,7 @@ class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
state.automations[index].id, state.automations[index].id,
cardType: 'automations', cardType: 'automations',
spaceName: spaceName:
state.automations[index].spaceName, state.scenes[index].spaceName,
onTap: () { onTap: () {
BlocProvider.of<RoutineBloc>(context) BlocProvider.of<RoutineBloc>(context)
.add( .add(

View File

@ -17,8 +17,6 @@ class _RoutineDevicesState extends State<RoutineDevices> {
context.read<RoutineBloc>().add(FetchDevicesInRoutine()); context.read<RoutineBloc>().add(FetchDevicesInRoutine());
} }
static const _allowedProductTypes = {'AC', '1G', '2G', '3G', 'WPS', 'GW'};
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>( return BlocBuilder<RoutineBloc, RoutineState>(
@ -33,41 +31,47 @@ class _RoutineDevicesState extends State<RoutineDevices> {
} }
}); });
final deviceList = state.devices final deviceList = state.devices.where((device) {
.where((device) => _allowedProductTypes.contains(device.productType)) const allowedProductTypes = {'AC', '1G', '2G', '3G', 'WPS', 'GW'};
.toList(); return allowedProductTypes.contains(device.productType);
}).toList();
return Wrap( return Wrap(
spacing: 10, spacing: 10,
runSpacing: 10, runSpacing: 10,
children: deviceList.asMap().entries.map((entry) { children: deviceList.asMap().entries.map((entry) {
final device = entry.value; final device = entry.value;
final deviceData = {
'device': device,
'imagePath': device.getDefaultIcon(device.productType),
'title': device.name ?? '',
'deviceId': device.uuid,
'productType': device.productType,
'functions': device.functions,
'uniqueCustomId': '',
};
if (state.searchText != null && state.searchText!.isNotEmpty) { if (state.searchText != null && state.searchText!.isNotEmpty) {
return device.name! return device.name!
.toLowerCase() .toLowerCase()
.contains(state.searchText!.toLowerCase()) .contains(state.searchText!.toLowerCase())
? DraggableCard( ? DraggableCard(
imagePath: deviceData['imagePath'] as String, imagePath: device.getDefaultIcon(device.productType),
title: deviceData['title'] as String, title: device.name ?? '',
deviceData: deviceData, deviceData: {
'device': device,
'imagePath': device.getDefaultIcon(device.productType),
'title': device.name ?? '',
'deviceId': device.uuid,
'productType': device.productType,
'functions': device.functions,
'uniqueCustomId': '',
},
) )
: const SizedBox.shrink(); : const SizedBox.shrink();
} else { } else {
return DraggableCard( return DraggableCard(
imagePath: deviceData['imagePath'] as String, imagePath: device.getDefaultIcon(device.productType),
title: deviceData['title'] as String, title: device.name ?? '',
deviceData: deviceData, deviceData: {
'device': device,
'imagePath': device.getDefaultIcon(device.productType),
'title': device.name ?? '',
'deviceId': device.uuid,
'productType': device.productType,
'functions': device.functions,
'uniqueCustomId': '',
},
); );
} }
}).toList(), }).toList(),

View File

@ -1,42 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class RoutineDialogFunctionListTile extends StatelessWidget {
const RoutineDialogFunctionListTile({
super.key,
required this.iconPath,
required this.operationName,
required this.onTap,
});
final String iconPath;
final String operationName;
final void Function() onTap;
@override
Widget build(BuildContext context) {
return ListTile(
leading: SvgPicture.asset(
iconPath,
width: 24,
height: 24,
placeholderBuilder: (context) => const SizedBox(
width: 24,
height: 24,
),
),
title: Text(
operationName,
style: context.textTheme.bodyMedium,
),
trailing: const Icon(
Icons.arrow_forward_ios,
size: 16,
color: ColorsManager.textGray,
),
onTap: onTap,
);
}
}

View File

@ -1,47 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class RoutineDialogSelectionListTile extends StatelessWidget {
const RoutineDialogSelectionListTile({
required this.iconPath,
required this.description,
required this.isSelected,
required this.onTap,
super.key,
});
final bool isSelected;
final String iconPath;
final String description;
final void Function() onTap;
@override
Widget build(BuildContext context) {
return ListTile(
leading: SvgPicture.asset(
iconPath,
width: 24,
height: 24,
placeholderBuilder: (context) => Container(
width: 24,
height: 24,
color: Colors.transparent,
),
),
title: Text(
description,
style: context.textTheme.bodyMedium,
),
trailing: Icon(
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
size: 24,
color: isSelected
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
),
onTap: onTap,
);
}
}

View File

@ -13,38 +13,29 @@ 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/bloc/functions_bloc/functions_bloc_bloc.dart';
class ACHelper { class ACHelper {
static Future<Map<String, dynamic>?> showACFunctionsDialog({ static Future<Map<String, dynamic>?> showACFunctionsDialog(
required BuildContext context, BuildContext context,
required List<DeviceFunction> functions, List<DeviceFunction> functions,
required AllDevicesModel? device, AllDevicesModel? device,
required List<DeviceFunctionData>? deviceSelectedFunctions, List<DeviceFunctionData>? deviceSelectedFunctions,
required String uniqueCustomId, String uniqueCustomId,
required bool? removeComparetors, bool? removeComparetors,
required String dialogType, ) async {
}) async { List<ACFunction> acFunctions = functions.whereType<ACFunction>().toList();
List<ACFunction> acFunctions =
functions.whereType<ACFunction>().where((function) {
if (dialogType == 'THEN') {
return function.type == 'THEN' || function.type == 'BOTH';
}
return function.type == 'IF' || function.type == 'BOTH';
}).toList();
// List<ACFunction> acFunctions = functions.whereType<ACFunction>().toList();
return showDialog<Map<String, dynamic>?>( return showDialog<Map<String, dynamic>?>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return BlocProvider( return BlocProvider(
create: (_) => FunctionBloc() create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog( child: AlertDialog(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>( content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) { builder: (context, state) {
final selectedFunction = state.selectedFunction; final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName; final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = state.addedFunctions final selectedFunctionData =
.firstWhere((f) => f.functionCode == selectedFunction, state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData( orElse: () => DeviceFunctionData(
entityId: '', entityId: '',
functionCode: selectedFunction ?? '', functionCode: selectedFunction ?? '',
@ -74,10 +65,8 @@ class ACHelper {
child: _buildFunctionsList( child: _buildFunctionsList(
context: context, context: context,
acFunctions: acFunctions, acFunctions: acFunctions,
onFunctionSelected: onFunctionSelected: (functionCode, operationName) =>
(functionCode, operationName) => context context.read<FunctionBloc>().add(SelectFunction(
.read<FunctionBloc>()
.add(SelectFunction(
functionCode: functionCode, functionCode: functionCode,
operationName: operationName, operationName: operationName,
)), )),
@ -205,8 +194,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(
@ -302,8 +290,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(),
); );
} }
@ -397,13 +384,9 @@ 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.textGray,
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
if (!isSelected) { if (!isSelected) {
@ -415,8 +398,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,
), ),
), ),
); );

View File

@ -1,130 +1,28 @@
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/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/functions_bloc/functions_bloc_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/pages/routines/models/gateway.dart'; import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/gateway/gateway_if_dialog.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/routine_dialogs/gateway/gateway_dialog_value_selector.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/gateway/gateway_functions_list.dart';
class GatewayDialog extends StatefulWidget { final class GatewayHelper {
const GatewayDialog({ static Future<Map<String, dynamic>?> showGatewayFunctionsDialog({
required this.uniqueCustomId, required BuildContext context,
required this.functions, required List<DeviceFunction> functions,
required this.deviceSelectedFunctions, required String? uniqueCustomId,
required this.device, required List<DeviceFunctionData> deviceSelectedFunctions,
super.key, }) async {
}); return showDialog(
context: context,
final String? uniqueCustomId; builder: (context) => BlocProvider<FunctionBloc>(
final List<DeviceFunction> functions; create: (context) => FunctionBloc()
final List<DeviceFunctionData> deviceSelectedFunctions; ..add(
final AllDevicesModel? device; InitializeFunctions(deviceSelectedFunctions),
@override
State<GatewayDialog> createState() => _GatewayDialogState();
}
class _GatewayDialogState extends State<GatewayDialog> {
late final List<GatewayFunctions> _gatewayFunctions;
@override
void initState() {
super.initState();
_gatewayFunctions = widget.functions.whereType<GatewayFunctions>().toList();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) {
final selectedFunction = state.selectedFunction;
return Container(
width: selectedFunction != null ? 600 : 360,
height: 450,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.only(top: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const DialogHeader('Gateway Conditions'),
Expanded(child: _buildMainContent(context, state)),
_buildDialogFooter(context, state),
],
),
);
},
),
);
}
Widget _buildMainContent(BuildContext context, FunctionBlocState state) {
final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = state.addedFunctions.firstWhere(
(f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData(
entityId: '',
functionCode: selectedFunction ?? '',
operationName: '',
value: null,
),
);
final selectedGatewayFunctions = _gatewayFunctions.firstWhere(
(f) => f.code == selectedFunction,
orElse: () => GatewaySwitchAlarmSound(
deviceId: '',
deviceName: '',
type: '',
),
);
final operations = selectedGatewayFunctions.getOperationalValues();
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
GatewayFunctionsList(gatewayFunctions: _gatewayFunctions),
if (state.selectedFunction != null)
Expanded(
child: GatewayDialogValueSelector(
operations: operations,
selectedFunction: selectedFunction ?? '',
selectedFunctionData: selectedFunctionData,
gatewayFunctions: _gatewayFunctions,
operationName: selectedOperationName ?? '',
device: widget.device,
),
), ),
], child: GatewayIfDialog(
); uniqueCustomId: uniqueCustomId,
} functions: functions,
deviceSelectedFunctions: deviceSelectedFunctions),
Widget _buildDialogFooter(BuildContext context, FunctionBlocState state) { ),
return DialogFooter(
onCancel: () => Navigator.pop(context),
onConfirm: state.addedFunctions.isNotEmpty
? () {
context.read<RoutineBloc>().add(
AddFunctionToRoutine(
state.addedFunctions,
widget.uniqueCustomId ?? '-1',
),
);
Navigator.pop(
context,
{'deviceId': widget.functions.firstOrNull?.deviceId},
);
}
: null,
isConfirmEnabled: state.selectedFunction != null,
); );
} }
} }

View File

@ -1,58 +0,0 @@
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/device_functions.dart';
import 'package:syncrow_web/pages/routines/models/gateway.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialog_selection_list_tile.dart';
class GatewayDialogValueSelector extends StatelessWidget {
const GatewayDialogValueSelector({
required this.operations,
required this.selectedFunction,
required this.selectedFunctionData,
required this.gatewayFunctions,
required this.device,
required this.operationName,
super.key,
});
final List<GatewayOperationalValue> operations;
final String selectedFunction;
final DeviceFunctionData? selectedFunctionData;
final List<GatewayFunctions> gatewayFunctions;
final AllDevicesModel? device;
final String operationName;
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: operations.length,
itemBuilder: (context, index) {
final operation = operations[index];
final isSelected = selectedFunctionData?.value == operation.value;
return RoutineDialogSelectionListTile(
iconPath: operation.icon,
description: operation.description,
isSelected: isSelected,
onTap: () {
if (!isSelected) {
context.read<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: device?.uuid ?? '',
functionCode: selectedFunction,
operationName: operationName,
value: operation.value,
condition: selectedFunctionData?.condition,
valueDescription: selectedFunctionData?.valueDescription,
),
),
);
}
},
);
},
);
}
}

View File

@ -1,43 +0,0 @@
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/gateway.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialog_function_list_tile.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class GatewayFunctionsList extends StatelessWidget {
const GatewayFunctionsList({
required this.gatewayFunctions,
super.key,
});
final List<GatewayFunctions> gatewayFunctions;
@override
Widget build(BuildContext context) {
return SizedBox(
width: 360,
child: ListView.separated(
shrinkWrap: false,
itemCount: gatewayFunctions.length,
separatorBuilder: (context, index) => const Padding(
padding: EdgeInsets.symmetric(horizontal: 40.0),
child: Divider(color: ColorsManager.dividerColor),
),
itemBuilder: (context, index) {
final function = gatewayFunctions[index];
return RoutineDialogFunctionListTile(
iconPath: function.icon,
operationName: function.operationName,
onTap: () => context.read<FunctionBloc>().add(
SelectFunction(
functionCode: function.code,
operationName: function.operationName,
),
),
);
},
),
);
}
}

View File

@ -1,34 +0,0 @@
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/device_functions.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/gateway/gateway_dialog.dart';
abstract final class GatewayHelper {
const GatewayHelper._();
static Future<Map<String, dynamic>?> showGatewayFunctionsDialog({
required BuildContext context,
required List<DeviceFunction> functions,
required String? uniqueCustomId,
required List<DeviceFunctionData> deviceSelectedFunctions,
required AllDevicesModel? device,
}) async {
return showDialog(
context: context,
builder: (context) => BlocProvider<FunctionBloc>(
create: (context) => FunctionBloc()
..add(
InitializeFunctions(deviceSelectedFunctions),
),
child: GatewayDialog(
uniqueCustomId: uniqueCustomId,
functions: functions,
deviceSelectedFunctions: deviceSelectedFunctions,
device: device,
),
),
);
}
}

View File

@ -0,0 +1,243 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.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/models/device_functions.dart';
import 'package:syncrow_web/pages/routines/models/gateway.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/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class GatewayIfDialog extends StatefulWidget {
const GatewayIfDialog({
required this.uniqueCustomId,
required this.functions,
required this.deviceSelectedFunctions,
super.key,
});
final String? uniqueCustomId;
final List<DeviceFunction> functions;
final List<DeviceFunctionData> deviceSelectedFunctions;
@override
State<GatewayIfDialog> createState() => _GatewayIfDialogState();
}
class _GatewayIfDialogState extends State<GatewayIfDialog> {
late final List<GatewayFunctions> _gatewayFunctions;
@override
void initState() {
super.initState();
_gatewayFunctions = widget.functions.whereType<GatewayFunctions>().toList();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) {
final selectedFunction = state.selectedFunction;
return Container(
width: selectedFunction != null ? 600 : 360,
height: 450,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.only(top: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const DialogHeader('Gateway Conditions'),
Expanded(child: _buildMainContent(context, state)),
_buildDialogFooter(context, state),
],
),
);
},
),
);
}
Widget _buildDialogFooter(BuildContext context, FunctionBlocState state) {
return DialogFooter(
onCancel: () => Navigator.pop(context),
onConfirm: state.addedFunctions.isNotEmpty
? () {
context.read<RoutineBloc>().add(
AddFunctionToRoutine(
state.addedFunctions,
widget.uniqueCustomId ?? '-1',
),
);
Navigator.pop(
context,
{'deviceId': widget.functions.firstOrNull?.deviceId},
);
}
: null,
isConfirmEnabled: state.selectedFunction != null,
);
}
Widget _buildMainContent(BuildContext context, FunctionBlocState state) {
final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = state.addedFunctions.firstWhere(
(f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData(
entityId: '',
functionCode: selectedFunction ?? '',
operationName: '',
value: null,
),
);
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_buildFunctionList(context),
if (state.selectedFunction != null)
Expanded(
child: _buildValueSelector(
context: context,
selectedFunction: selectedFunction ?? '',
selectedFunctionData: selectedFunctionData,
acFunctions: _gatewayFunctions,
operationName: selectedOperationName ?? '',
),
),
],
);
}
Widget _buildFunctionList(BuildContext context) {
return SizedBox(
width: 360,
child: ListView.separated(
shrinkWrap: false,
itemCount: _gatewayFunctions.length,
separatorBuilder: (context, index) => const Padding(
padding: EdgeInsets.symmetric(horizontal: 40.0),
child: Divider(color: ColorsManager.dividerColor),
),
itemBuilder: (context, index) {
final function = _gatewayFunctions[index];
return ListTile(
leading: SvgPicture.asset(
function.icon,
width: 24,
height: 24,
placeholderBuilder: (context) => const SizedBox(
width: 24,
height: 24,
),
),
title: Text(
function.operationName,
style: context.textTheme.bodyMedium,
),
trailing: const Icon(
Icons.arrow_forward_ios,
size: 16,
color: ColorsManager.textGray,
),
onTap: () => context.read<FunctionBloc>().add(
SelectFunction(
functionCode: function.code,
operationName: function.operationName,
),
),
);
},
),
);
}
static Widget _buildValueSelector({
required BuildContext context,
required String selectedFunction,
required DeviceFunctionData? selectedFunctionData,
required List<GatewayFunctions> acFunctions,
AllDevicesModel? device,
required String operationName,
}) {
final selectedGatewayFunctions = acFunctions.firstWhere(
(f) => f.code == selectedFunction,
);
final values = selectedGatewayFunctions.getOperationalValues();
return _buildOperationalValuesList(
context: context,
values: values,
selectedValue: selectedFunctionData?.value,
device: device,
operationName: operationName,
selectCode: selectedFunction,
selectedFunctionData: selectedFunctionData,
);
}
static Widget _buildOperationalValuesList({
required BuildContext context,
required List<GatewayOperationalValue> values,
required dynamic selectedValue,
AllDevicesModel? device,
required String operationName,
required String selectCode,
DeviceFunctionData? selectedFunctionData,
}) {
return ListView.builder(
itemCount: values.length,
itemBuilder: (context, index) {
final value = values[index];
final isSelected = selectedValue == value.value;
return ListTile(
leading: SvgPicture.asset(
value.icon,
width: 24,
height: 24,
placeholderBuilder: (context) => Container(
width: 24,
height: 24,
color: Colors.transparent,
),
),
title: Text(
value.description,
style: context.textTheme.bodyMedium,
),
trailing: Icon(
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
size: 24,
color: isSelected
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
),
onTap: () {
if (!isSelected) {
context.read<FunctionBloc>().add(
AddFunction(
functionData: DeviceFunctionData(
entityId: device?.uuid ?? '',
functionCode: selectCode,
operationName: operationName,
value: value.value,
condition: selectedFunctionData?.condition,
valueDescription: selectedFunctionData?.valueDescription,
),
),
);
}
},
);
},
);
}
}

View File

@ -5,10 +5,8 @@ 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/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';
@ -16,32 +14,29 @@ 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';
class OneGangSwitchHelper { class OneGangSwitchHelper {
static Future<Map<String, dynamic>?> showSwitchFunctionsDialog({ static Future<Map<String, dynamic>?> showSwitchFunctionsDialog(
required String dialogType, BuildContext context,
required BuildContext context, List<DeviceFunction> functions,
required List<DeviceFunction> functions, AllDevicesModel? device,
required AllDevicesModel? device, List<DeviceFunctionData>? deviceSelectedFunctions,
required List<DeviceFunctionData>? deviceSelectedFunctions, String uniqueCustomId,
required String uniqueCustomId, bool removeComparetors,
required bool removeComparetors, ) async {
}) async { List<BaseSwitchFunction> acFunctions = functions.whereType<BaseSwitchFunction>().toList();
List<BaseSwitchFunction> oneGangFunctions =
functions.whereType<BaseSwitchFunction>().toList();
return showDialog<Map<String, dynamic>?>( return showDialog<Map<String, dynamic>?>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return BlocProvider( return BlocProvider(
create: (_) => FunctionBloc() create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog( child: AlertDialog(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>( content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) { builder: (context, state) {
final selectedFunction = state.selectedFunction; final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName; final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = state.addedFunctions final selectedFunctionData =
.firstWhere((f) => f.functionCode == selectedFunction, state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData( orElse: () => DeviceFunctionData(
entityId: '', entityId: '',
functionCode: selectedFunction ?? '', functionCode: selectedFunction ?? '',
@ -66,12 +61,12 @@ class OneGangSwitchHelper {
// Left side: Function list // Left side: Function list
Expanded( Expanded(
child: ListView.separated( child: ListView.separated(
itemCount: oneGangFunctions.length, itemCount: acFunctions.length,
separatorBuilder: (_, __) => const Divider( separatorBuilder: (_, __) => const Divider(
color: ColorsManager.dividerColor, color: ColorsManager.dividerColor,
), ),
itemBuilder: (context, index) { itemBuilder: (context, index) {
final function = oneGangFunctions[index]; final function = acFunctions[index];
return ListTile( return ListTile(
leading: SvgPicture.asset( leading: SvgPicture.asset(
function.icon, function.icon,
@ -88,12 +83,9 @@ class OneGangSwitchHelper {
color: ColorsManager.textGray, color: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
context context.read<FunctionBloc>().add(SelectFunction(
.read<FunctionBloc>()
.add(SelectFunction(
functionCode: function.code, functionCode: function.code,
operationName: operationName: function.operationName,
function.operationName,
)); ));
}, },
); );
@ -107,7 +99,7 @@ class OneGangSwitchHelper {
context: context, context: context,
selectedFunction: selectedFunction, selectedFunction: selectedFunction,
selectedFunctionData: selectedFunctionData, selectedFunctionData: selectedFunctionData,
acFunctions: oneGangFunctions, acFunctions: acFunctions,
device: device, device: device,
operationName: selectedOperationName ?? '', operationName: selectedOperationName ?? '',
removeComparetors: removeComparetors, removeComparetors: removeComparetors,
@ -182,14 +174,8 @@ class OneGangSwitchHelper {
removeComparetors: removeComparetors, removeComparetors: removeComparetors,
); );
} }
final selectedFn = acFunctions.firstWhere(
(f) => f.code == selectedFunction,
orElse: () => OneGangSwitchFunction(
deviceId: '',
deviceName: '',
),
);
final selectedFn = acFunctions.firstWhere((f) => f.code == selectedFunction);
final values = selectedFn.getOperationalValues(); final values = selectedFn.getOperationalValues();
return _buildOperationalValuesList( return _buildOperationalValuesList(
@ -226,11 +212,11 @@ class OneGangSwitchHelper {
selectedFunctionData, selectedFunctionData,
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownDisplay(context, initialValue, device, operationName, _buildCountDownDisplay(
selectedFunctionData, selectCode), context, initialValue, device, operationName, selectedFunctionData, selectCode),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownSlider(context, initialValue, device, operationName, _buildCountDownSlider(
selectedFunctionData, selectCode), context, initialValue, device, operationName, selectedFunctionData, selectCode),
], ],
); );
} }
@ -271,8 +257,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(),
); );
} }
@ -320,8 +305,7 @@ class OneGangSwitchHelper {
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.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
(operationalValues.minValue ?? 0)) /
(operationalValues.stepValue ?? 1)) (operationalValues.stepValue ?? 1))
.round(), .round(),
onChanged: (value) { onChanged: (value) {
@ -373,13 +357,9 @@ 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.textGray,
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
if (!isSelected) { if (!isSelected) {
@ -391,8 +371,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,
), ),
), ),
); );

View File

@ -14,15 +14,14 @@ 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';
class ThreeGangSwitchHelper { class ThreeGangSwitchHelper {
static Future<Map<String, dynamic>?> showSwitchFunctionsDialog({ static Future<Map<String, dynamic>?> showSwitchFunctionsDialog(
required BuildContext context, BuildContext context,
required List<DeviceFunction> functions, List<DeviceFunction> functions,
required AllDevicesModel? device, AllDevicesModel? device,
required List<DeviceFunctionData>? deviceSelectedFunctions, List<DeviceFunctionData>? deviceSelectedFunctions,
required String uniqueCustomId, String uniqueCustomId,
required String dialogType, bool removeComparetors,
required bool removeComparetors, ) async {
}) async {
List<BaseSwitchFunction> switchFunctions = List<BaseSwitchFunction> switchFunctions =
functions.whereType<BaseSwitchFunction>().toList(); functions.whereType<BaseSwitchFunction>().toList();

View File

@ -14,32 +14,29 @@ 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';
class TwoGangSwitchHelper { class TwoGangSwitchHelper {
static Future<Map<String, dynamic>?> showSwitchFunctionsDialog({ static Future<Map<String, dynamic>?> showSwitchFunctionsDialog(
required BuildContext context, BuildContext context,
required List<DeviceFunction> functions, List<DeviceFunction> functions,
required AllDevicesModel? device, AllDevicesModel? device,
required List<DeviceFunctionData>? deviceSelectedFunctions, List<DeviceFunctionData>? deviceSelectedFunctions,
required String uniqueCustomId, String uniqueCustomId,
required bool removeComparetors, bool removeComparetors,
required String dialogType, ) async {
}) async { List<BaseSwitchFunction> switchFunctions = functions.whereType<BaseSwitchFunction>().toList();
List<BaseSwitchFunction> switchFunctions =
functions.whereType<BaseSwitchFunction>().toList();
return showDialog<Map<String, dynamic>?>( return showDialog<Map<String, dynamic>?>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return BlocProvider( return BlocProvider(
create: (_) => FunctionBloc() create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog( child: AlertDialog(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>( content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) { builder: (context, state) {
final selectedFunction = state.selectedFunction; final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName; final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = state.addedFunctions final selectedFunctionData =
.firstWhere((f) => f.functionCode == selectedFunction, state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData( orElse: () => DeviceFunctionData(
entityId: '', entityId: '',
functionCode: selectedFunction ?? '', functionCode: selectedFunction ?? '',
@ -86,12 +83,9 @@ class TwoGangSwitchHelper {
color: ColorsManager.textGray, color: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
context context.read<FunctionBloc>().add(SelectFunction(
.read<FunctionBloc>()
.add(SelectFunction(
functionCode: function.code, functionCode: function.code,
operationName: operationName: function.operationName,
function.operationName,
)); ));
}, },
); );
@ -167,8 +161,7 @@ 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 ?? 200; final initialValue = selectedFunctionData?.value ?? 200;
return _buildTemperatureSelector( return _buildTemperatureSelector(
context: context, context: context,
@ -182,8 +175,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(
@ -220,11 +212,11 @@ class TwoGangSwitchHelper {
selectedFunctionData, selectedFunctionData,
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownDisplay(context, initialValue, device, operationName, _buildCountDownDisplay(
selectedFunctionData, selectCode), context, initialValue, device, operationName, selectedFunctionData, selectCode),
const SizedBox(height: 20), const SizedBox(height: 20),
_buildCountDownSlider(context, initialValue, device, operationName, _buildCountDownSlider(
selectedFunctionData, selectCode), context, initialValue, device, operationName, selectedFunctionData, selectCode),
], ],
); );
} }
@ -265,8 +257,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(),
); );
} }
@ -314,8 +305,7 @@ class TwoGangSwitchHelper {
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.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
(operationalValues.minValue ?? 0)) /
(operationalValues.stepValue ?? 1)) (operationalValues.stepValue ?? 1))
.round(), .round(),
onChanged: (value) { onChanged: (value) {
@ -367,13 +357,9 @@ 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.textGray,
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
), ),
onTap: () { onTap: () {
if (!isSelected) { if (!isSelected) {
@ -385,8 +371,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,
), ),
), ),
); );

View File

@ -1,3 +1,4 @@
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';
@ -27,12 +28,9 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_hoursController = _hoursController = FixedExtentScrollController(initialItem: widget.initialHours);
FixedExtentScrollController(initialItem: widget.initialHours); _minutesController = FixedExtentScrollController(initialItem: widget.initialMinutes);
_minutesController = _secondsController = FixedExtentScrollController(initialItem: widget.initialSeconds);
FixedExtentScrollController(initialItem: widget.initialMinutes);
_secondsController =
FixedExtentScrollController(initialItem: widget.initialSeconds);
} }
@override @override
@ -49,8 +47,6 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
} }
} }
@override @override
void dispose() { void dispose() {
_hoursController.dispose(); _hoursController.dispose();
@ -65,28 +61,26 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
_buildPickerColumn( _buildPickerColumn(
label: 'h', label: 'h',
controller: _hoursController, controller: _hoursController,
itemCount: 3, itemCount: 24,
onChanged: (value) { onChanged: (value) => _handleTimeChange(
_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) { onChanged: (value) => _handleTimeChange(
_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',
@ -103,19 +97,6 @@ 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);
} }
@ -166,4 +147,4 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
], ],
); );
} }
} }

View File

@ -1,51 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.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/create_community/view/create_community_dialog.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.existingCommunityNames,
super.key,
});
final List<String> existingCommunityNames;
@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: () => _showCreateCommunityDialog(context),
icon: SvgPicture.asset(Assets.addIcon),
),
);
}
void _showCreateCommunityDialog(BuildContext context) => showDialog<void>(
context: context,
builder: (context) => CreateCommunityDialog(
isEditMode: false,
existingCommunityNames: existingCommunityNames,
onCreateCommunity: (name, description) {
context.read<SpaceManagementBloc>().add(
CreateCommunityEvent(name, description, context),
);
},
),
);
}

View File

@ -1,31 +0,0 @@
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.existingCommunityNames, super.key});
final List<String> existingCommunityNames;
@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(existingCommunityNames: existingCommunityNames),
],
),
);
}
}

View File

@ -1,15 +1,17 @@
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/search_bar.dart'; import 'package:syncrow_web/common/widgets/search_bar.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/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';
class SidebarWidget extends StatefulWidget { class SidebarWidget extends StatefulWidget {
@ -17,59 +19,62 @@ class SidebarWidget extends StatefulWidget {
final String? selectedSpaceUuid; final String? selectedSpaceUuid;
const SidebarWidget({ const SidebarWidget({
super.key,
required this.communities, required this.communities,
this.selectedSpaceUuid, this.selectedSpaceUuid,
super.key,
}); });
@override @override
State<SidebarWidget> createState() => _SidebarWidgetState(); _SidebarWidgetState createState() => _SidebarWidgetState();
} }
class _SidebarWidgetState extends State<SidebarWidget> { class _SidebarWidgetState extends State<SidebarWidget> {
String _searchQuery = ''; String _searchQuery = ''; // Track search query
String? _selectedSpaceUuid; String? _selectedSpaceUuid;
String? _selectedId; String? _selectedId;
@override @override
void initState() { void initState() {
_selectedId = widget.selectedSpaceUuid;
super.initState(); super.initState();
_selectedId = widget.selectedSpaceUuid; // Initialize with the passed selected space UUID
} }
@override @override
void didUpdateWidget(covariant SidebarWidget oldWidget) { void didUpdateWidget(covariant SidebarWidget oldWidget) {
if (widget.selectedSpaceUuid != oldWidget.selectedSpaceUuid) {
setState(() => _selectedId = widget.selectedSpaceUuid);
}
super.didUpdateWidget(oldWidget); super.didUpdateWidget(oldWidget);
if (widget.selectedSpaceUuid != oldWidget.selectedSpaceUuid) {
setState(() {
_selectedId = widget.selectedSpaceUuid;
});
}
} }
List<CommunityModel> _filteredCommunities() { // Function to filter communities based on the search query
List<CommunityModel> _filterCommunities() {
if (_searchQuery.isEmpty) { if (_searchQuery.isEmpty) {
// Reset the selected community and space UUIDs if there's no query
_selectedSpaceUuid = null; _selectedSpaceUuid = null;
return widget.communities; return widget.communities;
} }
// Filter communities and expand only those that match the query
return widget.communities.where((community) { return widget.communities.where((community) {
final containsQueryInCommunity = final containsQueryInCommunity =
community.name.toLowerCase().contains(_searchQuery.toLowerCase()); community.name.toLowerCase().contains(_searchQuery.toLowerCase());
final containsQueryInSpaces = community.spaces.any((space) => final containsQueryInSpaces =
_containsQuery(space: space, query: _searchQuery.toLowerCase())); community.spaces.any((space) => _containsQuery(space, _searchQuery.toLowerCase()));
return containsQueryInCommunity || containsQueryInSpaces; return containsQueryInCommunity || containsQueryInSpaces;
}).toList(); }).toList();
} }
bool _containsQuery({ // Helper function to determine if any space or its children match the search query
required SpaceModel space, bool _containsQuery(SpaceModel space, String query) {
required String query,
}) {
final matchesSpace = space.name.toLowerCase().contains(query); final matchesSpace = space.name.toLowerCase().contains(query);
final matchesChildren = space.children.any( final matchesChildren =
(child) => _containsQuery(space: child, query: query), 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) { if (matchesSpace || matchesChildren) {
_selectedSpaceUuid = space.uuid; _selectedSpaceUuid = space.uuid;
} }
@ -78,35 +83,79 @@ class _SidebarWidgetState extends State<SidebarWidget> {
} }
bool _isSpaceOrChildSelected(SpaceModel space) { bool _isSpaceOrChildSelected(SpaceModel space) {
final isSpaceSelected = _selectedSpaceUuid == space.uuid; // Return true if the current space or any of its child spaces is selected
final anySubSpaceIsSelected = space.children.any(_isSpaceOrChildSelected); if (_selectedSpaceUuid == space.uuid) {
return isSpaceSelected || anySubSpaceIsSelected; return true;
}
// Recursively check if any child spaces match the query
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 = _filteredCommunities(); final filteredCommunities = _filterCommunities();
return Container( return Container(
width: 300, width: 300,
decoration: subSectionContainerDecoration, decoration: subSectionContainerDecoration,
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min, // Ensures the Column only takes necessary height
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SidebarHeader( // Communities title with the add button
existingCommunityNames: Container(
widget.communities.map((community) => community.name).toList(), 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) => setState(() => _searchQuery = query), onSearchChanged: (query) {
setState(() {
_searchQuery = query;
});
},
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
// Community list
Expanded( Expanded(
child: ListView( child: ListView(
children: filteredCommunities children: filteredCommunities.map((community) {
.map((community) => _buildCommunityTile(context, community)) return _buildCommunityTile(context, community);
.toList(), }).toList(),
), ),
), ),
], ],
@ -114,7 +163,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;
return CommunityTile( return CommunityTile(
title: community.name, title: community.name,
key: ValueKey(community.uuid), key: ValueKey(community.uuid),
@ -123,7 +183,7 @@ class _SidebarWidgetState extends State<SidebarWidget> {
onItemSelected: () { onItemSelected: () {
setState(() { setState(() {
_selectedId = community.uuid; _selectedId = community.uuid;
_selectedSpaceUuid = null; _selectedSpaceUuid = null; // Update the selected community
}); });
context.read<CenterBodyBloc>().add(CommunitySelectedEvent()); context.read<CenterBodyBloc>().add(CommunitySelectedEvent());
@ -132,51 +192,46 @@ class _SidebarWidgetState extends State<SidebarWidget> {
SelectCommunityEvent(selectedCommunity: community), SelectCommunityEvent(selectedCommunity: community),
); );
}, },
onExpansionChanged: (title, expanded) {}, onExpansionChanged: (String title, bool expanded) {
children: community.spaces _handleExpansionChange(community.uuid, expanded);
.where((space) { },
final isDeleted = space.status != SpaceStatus.deleted; children: hasChildren
final isParentDeleted = space.status != SpaceStatus.parentDeleted; ? community.spaces
return (isDeleted || isParentDeleted); .where((space) => (space.status != SpaceStatus.deleted ||
}) space.status != SpaceStatus.parentDeleted))
.map((space) => _buildSpaceTile(space: space, community: community)) .map((space) => _buildSpaceTile(space, community))
.toList(), .toList()
: null,
); );
} }
Widget _buildSpaceTile({ Widget _buildSpaceTile(SpaceModel space, CommunityModel community, {int depth = 1}) {
required SpaceModel space, bool isExpandedSpace = _isSpaceOrChildSelected(space);
required CommunityModel community,
}) {
final spaceIsExpanded = _isSpaceOrChildSelected(space);
final isSelected = _selectedId == space.uuid;
return Padding( return Padding(
padding: const EdgeInsetsDirectional.only(start: 16.0), padding: EdgeInsets.only(left: depth * 16.0),
child: SpaceTile( child: SpaceTile(
title: space.name, title: space.name,
key: ValueKey(space.uuid), key: ValueKey(space.uuid),
isSelected: isSelected, isSelected: _selectedId == space.uuid,
initiallyExpanded: spaceIsExpanded, initiallyExpanded: isExpandedSpace,
onExpansionChanged: (expanded) {}, onExpansionChanged: (bool expanded) {
onItemSelected: () { _handleExpansionChange(space.uuid ?? '', expanded);
setState(() { },
_selectedId = space.uuid; onItemSelected: () {
_selectedSpaceUuid = space.uuid; setState(() {
}); _selectedId = space.uuid;
_selectedSpaceUuid = space.uuid;
});
context.read<SpaceManagementBloc>().add( context.read<SpaceManagementBloc>().add(
SelectSpaceEvent(selectedCommunity: community, selectedSpace: space), SelectSpaceEvent(selectedCommunity: community, selectedSpace: space),
); );
}, },
children: space.children children: space.children.isNotEmpty
.map( ? space.children.map((childSpace) => _buildSpaceTile(childSpace, community)).toList()
(childSpace) => _buildSpaceTile( : [], // Recursively render child spaces if available
space: childSpace, ));
community: community,
),
)
.toList(),
),
);
} }
void _handleExpansionChange(String uuid, bool expanded) {}
} }