diff --git a/lib/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart b/lib/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart index 9f6ebd4..e14b181 100644 --- a/lib/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart +++ b/lib/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart @@ -1,5 +1,7 @@ import 'dart:math'; +import 'package:day_picker/model/day_in_week.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_event.dart'; import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart'; @@ -11,13 +13,52 @@ import 'package:syncrow_app/services/api/devices_api.dart'; class SmartDoorBloc extends Bloc { final String deviceId; late SmartDoorModel deviceStatus; + TextEditingController passwordController = TextEditingController(); + TextEditingController passwordNameController = TextEditingController(); + String effectiveTime = 'Select Time'; + int? effectiveTimeTimeStamp; + String expirationTime = 'Select Time'; + int? expirationTimeTimeStamp; + bool repeat = false; + bool isStartEndTime = true; + List? selectedDay; + + DateTime? startTime; + DateTime? endTime ; // This sets the date to December 31, 2050. + + + changeTime(val,isStartEndTime){ + if(isStartEndTime==true){ + startTime=val; + }else{ + endTime=val; + } + emit(changeTimeState()); + } + + bool repeatFunction() { + repeat = !repeat; + emit(IsRepeatState()); + return repeat; + } + + + bool isStartEndTimeFun(val) { + emit(LoadingInitialState()); + isStartEndTime = val; + emit(IsStartEndState()); + return isStartEndTime; + } + + SmartDoorBloc({required this.deviceId}) : super(InitialState()) { on(_fetchSmartDoorStatus); on(_updateLock); } - void _fetchSmartDoorStatus(InitialEvent event, Emitter emit) async { + void _fetchSmartDoorStatus( + InitialEvent event, Emitter emit) async { emit(LoadingInitialState()); try { var response = await DevicesAPI.getDeviceStatus(deviceId); @@ -37,7 +78,10 @@ class SmartDoorBloc extends Bloc { emit(LoadingNewSate(smartDoorModel: deviceStatus)); try { final response = await DevicesAPI.controlDevice( - DeviceControlModel(deviceId: deviceId, code: 'normal_open_switch', value: !event.value), + DeviceControlModel( + deviceId: deviceId, + code: 'normal_open_switch', + value: !event.value), deviceId); if (response['success'] ?? false) { @@ -47,13 +91,144 @@ class SmartDoorBloc extends Bloc { emit(UpdateState(smartDoorModel: deviceStatus)); } - int generate7DigitNumber() { + void generate7DigitNumber() async { + emit(LoadingInitialState()); + passwordController.clear(); Random random = Random(); int min = 1000000; // Minimum 7-digit number int max = 9999999; // Maximum 7-digit number - return min + random.nextInt(max - min + 1); + passwordController.text = (min + random.nextInt(max - min + 1)).toString(); + emit(GeneratePasswordState()); + } + + Future selectTime(BuildContext context, {required bool isEffective}) async { + final DateTime? picked = await showDatePicker( + context: context, + initialDate: DateTime.now(), + firstDate: DateTime(2015, 8), + lastDate: DateTime(2101), + ); + if (picked != null) { + final TimeOfDay? timePicked = await showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + ); + if (timePicked != null) { + final selectedDateTime = DateTime( + picked.year, + picked.month, + picked.day, + timePicked.hour, + timePicked.minute, + ); + if (isEffective) { + if (expirationTimeTimeStamp != null && + selectedDateTime.millisecondsSinceEpoch > + expirationTimeTimeStamp!) { + // Show error message + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'Effective Time cannot be later than Expiration Time.'), + ), + ); + } else { + effectiveTime = selectedDateTime.toString(); + effectiveTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; + } + } else { + if (effectiveTimeTimeStamp != null && + selectedDateTime.millisecondsSinceEpoch < + effectiveTimeTimeStamp!) { + // Show error message + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + 'Expiration Time cannot be earlier than Effective Time.'), + ), + ); + } else { + expirationTime = selectedDateTime.toString(); + expirationTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; + } + } + emit(TimeSelectedState()); + } + } } -} + Future saveFunction(BuildContext context,) async { + if (passwordController.text.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Password name required'), + backgroundColor: Colors.red, + ), + ); + return; + } + if (passwordNameController.text.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Password name required'), + backgroundColor: Colors.red, + ), + ); + return; + } + if (effectiveTime == 'Select Time' || effectiveTimeTimeStamp == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Select effective time'), + backgroundColor: Colors.red, + ), + ); + return; + } + if (expirationTime == 'Select Time' || expirationTimeTimeStamp == null) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Select expiration time'), + backgroundColor: Colors.red, + ), + ); + return; + } + } +//["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] + + List days = [ + DayInWeek( + "Sun", + dayKey: 'Sun', + ), + DayInWeek( + "Mon", + dayKey: 'Mon', + ), + DayInWeek( + "Tue", + isSelected: true, + dayKey: 'Tue' + ), + DayInWeek( + "Wed", + dayKey: 'Wed', + ), + DayInWeek( + "Thu", + dayKey: 'Thu', + ), + DayInWeek( + "Fri", + dayKey: 'Fri', + ), + DayInWeek( + "Sat", + dayKey: 'Sat', + ), + ]; + +} diff --git a/lib/features/devices/bloc/smart_door_bloc/smart_door_state.dart b/lib/features/devices/bloc/smart_door_bloc/smart_door_state.dart index 12cab76..447ab9a 100644 --- a/lib/features/devices/bloc/smart_door_bloc/smart_door_state.dart +++ b/lib/features/devices/bloc/smart_door_bloc/smart_door_state.dart @@ -36,3 +36,11 @@ class FailedState extends SmartDoorState { @override List get props => [errorMessage]; } + +class GeneratePasswordState extends SmartDoorState{} +class TimeSelectedState extends SmartDoorState{} +class IsRepeatState extends SmartDoorState{} +class IsStartEndState extends SmartDoorState{} +class changeStartTimeState extends SmartDoorState{} +class changeEndTimeState extends SmartDoorState{} +class changeTimeState extends SmartDoorState{} diff --git a/lib/features/devices/view/widgets/smart_door/create_temporary_password.dart b/lib/features/devices/view/widgets/smart_door/create_temporary_password.dart index 293791e..826b963 100644 --- a/lib/features/devices/view/widgets/smart_door/create_temporary_password.dart +++ b/lib/features/devices/view/widgets/smart_door/create_temporary_password.dart @@ -1,7 +1,10 @@ import 'package:day_picker/day_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:pin_code_fields/pin_code_fields.dart'; +import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_bloc.dart'; +import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; @@ -9,258 +12,279 @@ import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:time_picker_spinner/time_picker_spinner.dart'; -class CreateTemporaryPassword extends StatefulWidget { +class CreateTemporaryPassword extends StatelessWidget { const CreateTemporaryPassword({super.key}); - @override - State createState() => - _CreateTemporaryPasswordState(); -} - -class _CreateTemporaryPasswordState extends State { - List _days = [ - DayInWeek( - "Sun", - dayKey: '', - ), - DayInWeek( - "Mon", - dayKey: '', - ), - DayInWeek("Tue", isSelected: true, dayKey: ''), - DayInWeek( - "Wed", - dayKey: '', - ), - DayInWeek( - "Thu", - dayKey: '', - ), - DayInWeek( - "Fri", - dayKey: '', - ), - DayInWeek( - "Sat", - dayKey: '', - ), - ]; - DateTime dateTime = DateTime.now(); - @override Widget build(BuildContext context) { - TextEditingController? controller = TextEditingController(); - return DefaultScaffold( - title: 'Create Password', - actions: [TextButton(onPressed: () {}, child: const Text('Save'))], - child: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - BodyMedium( - text: - 'Save the password immediately. The password is not displayed in the app.', - fontWeight: FontWeight.normal, - fontColor: ColorsManager.grayColor, - ), - SizedBox( - height: 20, - ), - const BodyMedium( - text: '7-Digit Password', - fontWeight: FontWeight.normal, - fontColor: ColorsManager.grayColor, - ), - DefaultContainer( - padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5), - child: Row( + return BlocProvider( + create: (BuildContext context) => SmartDoorBloc(deviceId: ''), + child: BlocConsumer( + listener: (context, state) { + if (state is FailedState) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(state.errorMessage), + backgroundColor: Colors.red, + ), + ); + } + }, builder: (context, state) { + return DefaultScaffold( + title: 'Create Password', + actions: [ + TextButton( + onPressed: () { + BlocProvider.of(context).saveFunction(context); + }, + child: const Text('Save')) + ], + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( - child: PinCodeTextField( - keyboardType: TextInputType.phone, - length: 7, - enabled: true, - obscureText: false, - animationType: AnimationType.fade, - pinTheme: PinTheme( - shape: PinCodeFieldShape.underline, - fieldHeight: 45, - fieldWidth: 20, - activeFillColor: Colors.white, - disabledColor: Colors.grey, - activeColor: Colors.grey, - errorBorderColor: Colors.grey, - inactiveColor: Colors.grey, - inactiveFillColor: Colors.grey, - selectedColor: Colors.grey), - animationDuration: Duration(milliseconds: 300), - backgroundColor: Colors.white, - enableActiveFill: false, - controller: controller, - onCompleted: (v) { - print("Completed"); - }, - onChanged: (value) { - print(value); - }, - appContext: context, - )), - const SizedBox( - width: 10, + const BodyMedium( + text: 'Save the password immediately. The password is not displayed in the app.', + fontWeight: FontWeight.normal, + fontColor: ColorsManager.grayColor, ), - InkWell( - onTap: () {}, - child: const BodyMedium( - text: 'Generate Randomly', - fontWeight: FontWeight.bold, - fontColor: ColorsManager.primaryColor, - )) + const SizedBox( + height: 20, + ), + const BodyMedium( + text: '7-Digit Password', + fontWeight: FontWeight.normal, + fontColor: ColorsManager.grayColor, + ), + DefaultContainer( + padding: + const EdgeInsets.symmetric(horizontal: 5, vertical: 5), + child: Row( + children: [ + Expanded( + child: PinCodeTextField( + keyboardType: TextInputType.phone, + length: 7, + enabled: true, + obscureText: false, + animationType: AnimationType.fade, + pinTheme: PinTheme( + shape: PinCodeFieldShape.underline, + fieldHeight: 45, + fieldWidth: 20, + activeFillColor: Colors.white, + disabledColor: Colors.grey, + activeColor: Colors.grey, + errorBorderColor: Colors.grey, + inactiveColor: Colors.grey, + inactiveFillColor: Colors.grey, + selectedColor: Colors.grey), + animationDuration: Duration(milliseconds: 300), + backgroundColor: Colors.white, + enableActiveFill: false, + controller: BlocProvider.of(context).passwordController, + appContext: context, + )), + const SizedBox( + width: 10, + ), + InkWell( + onTap: () {BlocProvider.of(context).generate7DigitNumber();}, + child: const BodyMedium( + text: 'Generate Randomly', + fontWeight: FontWeight.bold, + fontColor: ColorsManager.primaryColor, + )) + ], + ), + ), + const SizedBox( + height: 20, + ), + DefaultContainer( + padding: const EdgeInsets.all(10), + child: Column( + children: [ + ListTile( + contentPadding: EdgeInsets.zero, + leading: const BodyMedium( + text: 'Password Name', + fontWeight: FontWeight.normal, + ), + trailing: SizedBox( + width: MediaQuery.of(context).size.width / 2, + child: TextFormField( + controller: BlocProvider.of(context).passwordNameController, + decoration: const InputDecoration(labelText: 'Enter The Name'), + )), + ), + const Divider(), + ListTile( + contentPadding: EdgeInsets.zero, + leading: const BodyMedium( + text: 'Effective Time', + fontWeight: FontWeight.normal, + ), + trailing: SizedBox( + width: MediaQuery.of(context).size.width / 2, + child: InkWell( + onTap: () { + BlocProvider.of(context).selectTime(context, isEffective: true); + }, + child: Text(BlocProvider.of(context).effectiveTime), + )) + ), + const Divider(), + ListTile( + contentPadding: EdgeInsets.zero, + leading: const BodyMedium( + text: 'Expiration Time', + fontWeight: FontWeight.normal, + ), + trailing: SizedBox( + width: MediaQuery.of(context).size.width / 2, + child: SizedBox( + width: MediaQuery.of(context).size.width / 2, + child: InkWell( + onTap: () { + BlocProvider.of(context).selectTime(context, isEffective: false); + }, + child: Text(BlocProvider.of(context).expirationTime), + )), + ), + ), + ], + )), + const SizedBox( + height: 20, + ), + DefaultContainer( + padding: + const EdgeInsets.symmetric(horizontal: 5, vertical: 5), + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: const BodyMedium( + text: 'Repeat', + fontWeight: FontWeight.normal, + ), + trailing: Transform.scale( + scale: .8, + child: CupertinoSwitch( + value: + BlocProvider.of(context).repeat, + onChanged: (value) { + BlocProvider.of(context).repeatFunction(); + }, + applyTheme: true, + )), + ), + ), + const SizedBox( + height: 20, + ), + BlocProvider.of(context).repeat + ? DefaultContainer( + padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + InkWell( + onTap: () { + BlocProvider.of(context).isStartEndTimeFun(true); + }, + child: BodyMedium( + text: 'Start', + fontColor: + BlocProvider.of(context).isStartEndTime == false + ? Colors.black : Colors.blue, + fontSize: 18, + ), + ), + InkWell( + onTap: () { + BlocProvider.of(context).isStartEndTimeFun(false); + }, + child: BodyMedium( + text: 'End', + fontColor: + BlocProvider.of(context).isStartEndTime + ? Colors.black : Colors.blue, + fontSize: 18, + ), + ) + ], + ), + ), + const Divider(), + SizedBox( + height: 80, + child: BlocProvider.of(context).isStartEndTime + ? TimePickerSpinner( + time: BlocProvider.of(context).startTime, + is24HourMode: false, + itemHeight: 40, + normalTextStyle: const TextStyle( + fontSize: 24, + ), + highlightedTextStyle: const TextStyle( + fontSize: 30, color: Colors.blue), + onTimeChange: (time) { + BlocProvider.of(context).startTime = time; + BlocProvider.of( + context).changeTime(time, BlocProvider.of(context).isStartEndTime); + }, + ) + : TimePickerSpinner( + time: BlocProvider.of(context).endTime, + is24HourMode: false, + itemHeight: 40, + normalTextStyle: const TextStyle( + fontSize: 24, + ), + highlightedTextStyle: const TextStyle( + fontSize: 24, color: Colors.blue), + onTimeChange: (time) { + BlocProvider.of(context).endTime = time; + BlocProvider.of(context).changeTime(time, BlocProvider.of(context).isStartEndTime); + }, + ), + ), + const Divider(), + const SizedBox(height: 20), + SelectWeekDays( + width: MediaQuery.of(context).size.width / 1, + fontSize: 10, + fontWeight: FontWeight.w600, + days: BlocProvider.of(context) + .days, + border: false, + selectedDayTextColor: Colors.black, + unSelectedDayTextColor: Colors.grey, + boxDecoration: BoxDecoration( + borderRadius: BorderRadius.circular(30.0), + color: Colors.white), + onSelect: (values) { + print(values); + BlocProvider.of(context) + .selectedDay = values; + }, + ), + ], + )) + : SizedBox(), + const SizedBox( + height: 40, + ) ], ), ), - const SizedBox( - height: 20, - ), - DefaultContainer( - padding: const EdgeInsets.all(10), - child: Column( - children: [ - ListTile( - contentPadding: EdgeInsets.zero, - leading: const BodyMedium( - text: 'Password Name', - fontWeight: FontWeight.normal, - ), - trailing: Container( - width: MediaQuery.of(context).size.width / 2, - child: TextFormField( - decoration: - InputDecoration(labelText: 'Enter The Name'), - )), - ), - Divider(), - ListTile( - contentPadding: EdgeInsets.zero, - leading: const BodyMedium( - text: 'Effective Time', - fontWeight: FontWeight.normal, - ), - trailing: Container( - width: MediaQuery.of(context).size.width / 2, - child: TextFormField( - enabled: false, - decoration: InputDecoration( - border: InputBorder.none, - labelText: 'Select Time'), - )), - ), - Divider(), - ListTile( - contentPadding: EdgeInsets.zero, - leading: const BodyMedium( - text: 'Select Time', - fontWeight: FontWeight.normal, - ), - trailing: Container( - width: MediaQuery.of(context).size.width / 2, - child: TextFormField( - enabled: false, - decoration: InputDecoration( - border: InputBorder.none, - labelText: 'Select Time'), - )), - ), - ], - )), - SizedBox( - height: 20, - ), - DefaultContainer( - padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5), - child: ListTile( - contentPadding: EdgeInsets.zero, - leading: const BodyMedium( - text: 'Repeat', - fontWeight: FontWeight.normal, - ), - trailing: Transform.scale( - scale: .8, - child: CupertinoSwitch( - value: true, - onChanged: (value) {}, - applyTheme: true, - )), - ), - ), - const SizedBox( - height: 20, - ), - DefaultContainer( - padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - TextButton( - onPressed: () {}, - child: const Text('Start'), - ), - TextButton( - onPressed: () {}, - child: const Text('End'), - ) - ], - ), - Divider(), - - SizedBox( - height: 80, - child: TimePickerSpinner( - spacing: 10, - locale: const Locale('en', ''), - time: dateTime, - is24HourMode: false, - itemHeight: 40, - normalTextStyle: const TextStyle( - fontSize: 24, - ), - highlightedTextStyle: - const TextStyle(fontSize: 24, color: Colors.blue), - onTimeChange: (time) { - setState(() { - dateTime = time; - }); - }, - ), - ), - Divider(), - SizedBox(height: 20), - SelectWeekDays( - width: MediaQuery.of(context).size.width/1, - fontSize: 10, - fontWeight: FontWeight.w600, - days: _days, - border: false, - selectedDayTextColor: Colors.black, - unSelectedDayTextColor: Colors.grey, - boxDecoration: BoxDecoration( - borderRadius: BorderRadius.circular(30.0), - color: Colors.white), - onSelect: (values) { - print(values); - }, - ), - - ], - )), - SizedBox(height: 40,) - - ], - ), - ), - ); + ); + })); } }