From 3e7b6839d97675562b94d6c88c6d77facd31f8da Mon Sep 17 00:00:00 2001 From: mohammad Date: Thu, 27 Jun 2024 16:59:05 +0300 Subject: [PATCH 1/8] CreateTemporaryPassword --- assets/icons/noValidPasswords.svg | 5 + assets/icons/oneTimePassword.svg | 5 + assets/icons/timeLimitedPassword.svg | 5 + assets/icons/timeLimitedPasswordIcon.svg | 12 + lib/features/auth/bloc/auth_cubit.dart | 5 +- .../bloc/smart_door_bloc/smart_door_bloc.dart | 12 + .../smart_door_bloc/smart_door_event.dart | 2 + .../smart_door/create_temporary_password.dart | 266 ++++++++++++++++++ .../view/widgets/smart_door/door_grid.dart | 3 +- .../widgets/smart_door/door_interface.dart | 2 +- .../smart_door/temporary_password_page.dart | 225 +++++++++++++++ .../smart_door/view_temporary_password.dart | 45 +++ lib/generated/assets.dart | 23 ++ lib/utils/resource_manager/color_manager.dart | 1 + pubspec.lock | 16 ++ pubspec.yaml | 3 +- 16 files changed, 623 insertions(+), 7 deletions(-) create mode 100644 assets/icons/noValidPasswords.svg create mode 100644 assets/icons/oneTimePassword.svg create mode 100644 assets/icons/timeLimitedPassword.svg create mode 100644 assets/icons/timeLimitedPasswordIcon.svg create mode 100644 lib/features/devices/view/widgets/smart_door/create_temporary_password.dart create mode 100644 lib/features/devices/view/widgets/smart_door/temporary_password_page.dart create mode 100644 lib/features/devices/view/widgets/smart_door/view_temporary_password.dart diff --git a/assets/icons/noValidPasswords.svg b/assets/icons/noValidPasswords.svg new file mode 100644 index 0000000..1a660df --- /dev/null +++ b/assets/icons/noValidPasswords.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/oneTimePassword.svg b/assets/icons/oneTimePassword.svg new file mode 100644 index 0000000..06bd9b5 --- /dev/null +++ b/assets/icons/oneTimePassword.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/timeLimitedPassword.svg b/assets/icons/timeLimitedPassword.svg new file mode 100644 index 0000000..be4fdbc --- /dev/null +++ b/assets/icons/timeLimitedPassword.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/timeLimitedPasswordIcon.svg b/assets/icons/timeLimitedPasswordIcon.svg new file mode 100644 index 0000000..60cc3d6 --- /dev/null +++ b/assets/icons/timeLimitedPasswordIcon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/lib/features/auth/bloc/auth_cubit.dart b/lib/features/auth/bloc/auth_cubit.dart index 7715b3a..526a148 100644 --- a/lib/features/auth/bloc/auth_cubit.dart +++ b/lib/features/auth/bloc/auth_cubit.dart @@ -275,10 +275,7 @@ class AuthCubit extends Cubit { getTokenAndValidate() async { emit(AuthTokenLoading()); - const storage = FlutterSecureStorage( - aOptions: AndroidOptions( - encryptedSharedPreferences: true, - )); + const storage = FlutterSecureStorage(); final firstLaunch = await SharedPreferencesHelper.readBoolFromSP(StringsManager.firstLaunch) ?? true; 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 de0b4c7..9f6ebd4 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,3 +1,5 @@ +import 'dart:math'; + 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'; @@ -44,4 +46,14 @@ class SmartDoorBloc extends Bloc { } catch (_) {} emit(UpdateState(smartDoorModel: deviceStatus)); } + + int generate7DigitNumber() { + Random random = Random(); + int min = 1000000; // Minimum 7-digit number + int max = 9999999; // Maximum 7-digit number + return min + random.nextInt(max - min + 1); + } + + } + diff --git a/lib/features/devices/bloc/smart_door_bloc/smart_door_event.dart b/lib/features/devices/bloc/smart_door_bloc/smart_door_event.dart index a266390..146931e 100644 --- a/lib/features/devices/bloc/smart_door_bloc/smart_door_event.dart +++ b/lib/features/devices/bloc/smart_door_bloc/smart_door_event.dart @@ -15,3 +15,5 @@ class UpdateLockEvent extends SmartDoorEvent { @override List get props => [value]; } + + 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 new file mode 100644 index 0000000..293791e --- /dev/null +++ b/lib/features/devices/view/widgets/smart_door/create_temporary_password.dart @@ -0,0 +1,266 @@ +import 'package:day_picker/day_picker.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:pin_code_fields/pin_code_fields.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'; +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 { + 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( + 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, + ), + InkWell( + onTap: () {}, + 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: 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,) + + ], + ), + ), + ); + } +} + diff --git a/lib/features/devices/view/widgets/smart_door/door_grid.dart b/lib/features/devices/view/widgets/smart_door/door_grid.dart index fc33595..d3542b3 100644 --- a/lib/features/devices/view/widgets/smart_door/door_grid.dart +++ b/lib/features/devices/view/widgets/smart_door/door_grid.dart @@ -3,6 +3,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:syncrow_app/features/devices/view/widgets/smart_door/members_management_view.dart'; import 'package:syncrow_app/features/devices/view/widgets/smart_door/smart_linkage_view.dart'; +import 'package:syncrow_app/features/devices/view/widgets/smart_door/temporary_password_page.dart'; import 'package:syncrow_app/features/devices/view/widgets/smart_door/unlocking_records_view.dart'; import 'package:syncrow_app/features/shared_widgets/default_container.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; @@ -79,7 +80,7 @@ List> doorLockButtons = [ { 'title': 'Temporary Password', 'image': Assets.assetsIconsDoorlockAssetsTemporaryPassword, - 'page': null, + 'page':const TemporaryPasswordPage() , }, { 'title': 'Smart Linkage', diff --git a/lib/features/devices/view/widgets/smart_door/door_interface.dart b/lib/features/devices/view/widgets/smart_door/door_interface.dart index d950bc1..2d9620f 100644 --- a/lib/features/devices/view/widgets/smart_door/door_interface.dart +++ b/lib/features/devices/view/widgets/smart_door/door_interface.dart @@ -36,7 +36,6 @@ class DoorInterface extends StatelessWidget { ), ); } - }, builder: (context, state) { SmartDoorModel smartDoorModel = SmartDoorModel( unlockFingerprint: 0, @@ -113,6 +112,7 @@ class DoorInterface extends StatelessWidget { smartDoorModel: smartDoorModel, ), const DoorLockGrid(), + ], ) ], diff --git a/lib/features/devices/view/widgets/smart_door/temporary_password_page.dart b/lib/features/devices/view/widgets/smart_door/temporary_password_page.dart new file mode 100644 index 0000000..4cf1920 --- /dev/null +++ b/lib/features/devices/view/widgets/smart_door/temporary_password_page.dart @@ -0,0 +1,225 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/features/devices/view/widgets/smart_door/view_temporary_password.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_large.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class TemporaryPasswordPage extends StatefulWidget { + const TemporaryPasswordPage({super.key}); + + @override + State createState() => _TemporaryPasswordPageState(); +} + +class _TemporaryPasswordPageState extends State { + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: 'Temporary Password', + child: Column( + children: [ + const SizedBox(height: 10), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const BodyLarge( + text: 'Online Password', + fontWeight: FontWeight.normal, + fontColor: Color(0xfff999999), + ), + const BodyMedium( + text: 'We recommend that you use the online password when the Wi-Fi network is stable.', + fontWeight: FontWeight.normal, + fontColor: ColorsManager.grayColor, + ), + const SizedBox(height: 10), + DefaultContainer( + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10), + child:ListTile( + contentPadding: EdgeInsets.zero, + leading: SvgPicture.asset( + Assets.timeLimitedPasswordIcon), + title: const BodyMedium( + text: 'Time-Limited Password', + fontWeight: FontWeight.normal, + ), + onTap: () { + Navigator.of(context).push(MaterialPageRoute(builder: (context) => const ViewTemporaryPassword(),)); + }, + trailing: const Icon( + Icons.arrow_forward_ios, + color: ColorsManager.greyColor, + size: 15, + ), + ), + // Row( + // children: [ + // Expanded( + // child: Row( + // children: [ + // SizedBox( + // width: 45, + // height: 40, + // child: SvgPicture.asset( + // Assets.timeLimitedPasswordIcon), + // ), + // const SizedBox(width: 15), + // const Expanded( + // child: + // BodyMedium( + // text: 'Time-Limited Password', + // fontWeight: FontWeight.normal, + // ), + // ), + // ], + // ), + // ), + // const Row( + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // Icon( + // Icons.arrow_forward_ios, + // color: ColorsManager.greyColor, + // size: 15, + // ), + // ], + // ), + // ], + // ), + ), + ], + ), + + const SizedBox(height: 10), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const BodyLarge( + text: 'Offline Password', + fontWeight: FontWeight.normal, + fontColor: Color(0xfff999999), + ), + const BodyMedium( + text: 'WWe recommend that you use the offline password when the Wi-Fi is disconnected or in an unstable Wi-Fi environment.', + fontWeight: FontWeight.normal, + fontColor: ColorsManager.grayColor, + ), + const SizedBox(height: 10), + DefaultContainer( + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20), + child: Column(children: [ + ListTile( + contentPadding: EdgeInsets.zero, + leading: SvgPicture.asset(Assets.oneTimePassword), + title: + const BodyMedium( + text: 'One-Time Password', + fontWeight: FontWeight.normal, + ), + onTap: () {}, + trailing: const Icon( + Icons.arrow_forward_ios, + color: ColorsManager.greyColor, + size: 15, + ), + ), + // Row( + // children: [ + // Expanded( + // child: Row( + // children: [ + // SizedBox( + // width: 45, + // height: 40, + // child: SvgPicture.asset( + // Assets.oneTimePassword), + // ), + // const SizedBox(width: 15), + // const Expanded( + // child: BodyMedium( + // text: 'One-Time Password', + // fontWeight: FontWeight.normal, + // ), + // ), + // ], + // ), + // ), + // const Row( + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // Icon( + // Icons.arrow_forward_ios, + // color: ColorsManager.greyColor, + // size: 15, + // ), + // ], + // ), + // ], + // ), + const Divider( ), + ListTile( + contentPadding: EdgeInsets.zero, + leading: SvgPicture.asset( + Assets.timeLimitedPassword), + title: BodyMedium( + text: 'Time-Limited Password', + fontWeight: FontWeight.normal, + ), + onTap: () {}, + trailing: const Icon( + Icons.arrow_forward_ios, + color: ColorsManager.greyColor, + size: 15, + ), + ), + // Row( + // children: [ + // Expanded( + // child: Row( + // children: [ + // SizedBox( + // width: 45, + // height: 40, + // child: SvgPicture.asset( + // Assets.timeLimitedPassword), + // + // ), + // const SizedBox(width: 15), + // const Expanded( + // child: BodyMedium( + // text: 'Time-Limited Password', + // fontWeight: FontWeight.normal, + // ), + // ), + // ], + // ), + // ), + // const Row( + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // Icon( + // Icons.arrow_forward_ios, + // color: ColorsManager.greyColor, + // size: 15, + // ), + // ], + // ), + // ], + // ), + ],) + ), + ], + ), + const Spacer(), + ], + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart b/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart new file mode 100644 index 0000000..b65641d --- /dev/null +++ b/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart @@ -0,0 +1,45 @@ + +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; +import 'package:syncrow_app/generated/assets.dart'; + +import 'create_temporary_password.dart'; + + +class ViewTemporaryPassword extends StatelessWidget { + const ViewTemporaryPassword({super.key}); + + @override + Widget build(BuildContext context) { + return DefaultScaffold( + title: 'Passwords', + actions: [ + IconButton( + onPressed: () { + Navigator.of(context).push(MaterialPageRoute(builder: (context) => const CreateTemporaryPassword(),)); + + }, + icon: const Icon(Icons.add) + ) + ], + child: Container( + child: Center( + child:Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + Assets.noValidPasswords + ), + const SizedBox(height: 10,), + const BodyMedium(text: 'No Valid Passwords') + ], + ), + ), + ) + );; + } +} diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index d39855d..7e427ae 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -378,6 +378,29 @@ class Assets { static const String assetsIconsLinkageIconsDoorLockAlarm = "assets/icons/linkageIcons/doorLockAlarm.svg"; + + /// Assets for assetsIconsLinkTimeLimitedPasswordIcon + /// assets/icons/timeLimitedPasswordIcon.svg + static const String timeLimitedPasswordIcon = + "assets/icons/timeLimitedPasswordIcon.svg"; + + + /// Assets for assetsIconsoneTimePassword + /// assets/icons/oneTimePassword.svg + static const String oneTimePassword = + "assets/icons/oneTimePassword.svg"; + + /// Assets for assetsIconsTimeLimitedPassword + /// assets/icons/timeLimitedPassword.svg + static const String timeLimitedPassword = + "assets/icons/timeLimitedPassword.svg"; + + + /// Assets for assetsIconsNoValidPasswords + /// assets/icons/noValidPasswords.svg + static const String noValidPasswords = + "assets/icons/noValidPasswords.svg"; + /// Assets for assetsIconsLinkageIconsFamilyHome /// assets/icons/linkageIcons/familyHome.svg static const String assetsIconsLinkageIconsFamilyHome = diff --git a/lib/utils/resource_manager/color_manager.dart b/lib/utils/resource_manager/color_manager.dart index 05a1069..7815e27 100644 --- a/lib/utils/resource_manager/color_manager.dart +++ b/lib/utils/resource_manager/color_manager.dart @@ -21,4 +21,5 @@ abstract class ColorsManager { static const Color slidingBlueColor = Color(0x99023DFE); static const Color blackColor = Color(0xFF000000); static const Color lightGreen = Color(0xFF00FF0A); + static const Color grayColor = Color(0xFF999999); } diff --git a/pubspec.lock b/pubspec.lock index 75ce64d..8b82f21 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -121,6 +121,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" + day_picker: + dependency: "direct main" + description: + name: day_picker + sha256: a0f73716d688b3643769db39e7626cae54026bec24c33a8ebf5a6f8c618a05f2 + url: "https://pub.dev" + source: hosted + version: "2.2.0" dio: dependency: "direct main" description: @@ -834,6 +842,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.0" + time_picker_spinner: + dependency: "direct main" + description: + name: time_picker_spinner + sha256: "53d824801d108890d22756501e7ade9db48b53dac1ec41580499dd4ebd128e3c" + url: "https://pub.dev" + source: hosted + version: "1.0.0" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0da1da4..6eadfc6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -20,7 +20,7 @@ dependencies: flutter_svg: ^2.0.10+1 fl_chart: ^0.66.2 sleek_circular_slider: ^2.0.1 - + day_picker: 2.2.0 # Utility Packages flutter_secure_storage: ^9.0.0 cached_network_image: ^3.3.1 @@ -42,6 +42,7 @@ dependencies: permission_handler: ^11.3.1 pin_code_fields: ^8.0.1 share_plus: ^9.0.0 + time_picker_spinner: ^1.0.0 dev_dependencies: flutter_test: From 1f936149007392410000c0558428fc45724300d3 Mon Sep 17 00:00:00 2001 From: mohammad Date: Fri, 28 Jun 2024 23:50:14 +0300 Subject: [PATCH 2/8] CreateTemporaryPassword --- .../bloc/smart_door_bloc/smart_door_bloc.dart | 185 ++++++- .../smart_door_bloc/smart_door_state.dart | 8 + .../smart_door/create_temporary_password.dart | 510 +++++++++--------- 3 files changed, 455 insertions(+), 248 deletions(-) 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,) - - ], - ), - ), - ); + ); + })); } } From 8d6d5da5cd366681821e32d14b18e7c3cc9872ae Mon Sep 17 00:00:00 2001 From: mohammad Date: Sat, 29 Jun 2024 00:32:19 +0300 Subject: [PATCH 3/8] CreateTemporaryPassword --- .../bloc/smart_door_bloc/smart_door_bloc.dart | 7 +- .../smart_door/create_temporary_password.dart | 144 ++++++++++-------- 2 files changed, 84 insertions(+), 67 deletions(-) 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 e14b181..29d8a80 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 @@ -24,10 +24,11 @@ class SmartDoorBloc extends Bloc { List? selectedDay; DateTime? startTime; - DateTime? endTime ; // This sets the date to December 31, 2050. + DateTime? endTime ; changeTime(val,isStartEndTime){ + emit(LoadingInitialState()); if(isStartEndTime==true){ startTime=val; }else{ @@ -37,6 +38,7 @@ class SmartDoorBloc extends Bloc { } bool repeatFunction() { + emit(LoadingInitialState()); repeat = !repeat; emit(IsRepeatState()); return repeat; @@ -195,10 +197,7 @@ class SmartDoorBloc extends Bloc { ); return; } - } -//["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] - List days = [ DayInWeek( "Sun", 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 826b963..93856b2 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 @@ -8,13 +8,18 @@ import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_sta 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'; -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 StatelessWidget { +class CreateTemporaryPassword extends StatefulWidget { const CreateTemporaryPassword({super.key}); + @override + State createState() => + _CreateTemporaryPasswordState(); +} + +class _CreateTemporaryPasswordState extends State { @override Widget build(BuildContext context) { return BlocProvider( @@ -45,7 +50,8 @@ class CreateTemporaryPassword extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ const BodyMedium( - text: 'Save the password immediately. The password is not displayed in the app.', + text: + 'Save the password immediately. The password is not displayed in the app.', fontWeight: FontWeight.normal, fontColor: ColorsManager.grayColor, ), @@ -79,7 +85,8 @@ class CreateTemporaryPassword extends StatelessWidget { errorBorderColor: Colors.grey, inactiveColor: Colors.grey, inactiveFillColor: Colors.grey, - selectedColor: Colors.grey), + selectedColor: Colors.grey + ), animationDuration: Duration(milliseconds: 300), backgroundColor: Colors.white, enableActiveFill: false, @@ -90,7 +97,9 @@ class CreateTemporaryPassword extends StatelessWidget { width: 10, ), InkWell( - onTap: () {BlocProvider.of(context).generate7DigitNumber();}, + onTap: () { + BlocProvider.of(context).generate7DigitNumber(); + }, child: const BodyMedium( text: 'Generate Randomly', fontWeight: FontWeight.bold, @@ -115,8 +124,11 @@ class CreateTemporaryPassword extends StatelessWidget { trailing: SizedBox( width: MediaQuery.of(context).size.width / 2, child: TextFormField( - controller: BlocProvider.of(context).passwordNameController, - decoration: const InputDecoration(labelText: 'Enter The Name'), + controller: + BlocProvider.of(context) + .passwordNameController, + decoration: const InputDecoration( + labelText: 'Enter The Name'), )), ), const Divider(), @@ -130,10 +142,13 @@ class CreateTemporaryPassword extends StatelessWidget { width: MediaQuery.of(context).size.width / 2, child: InkWell( onTap: () { - BlocProvider.of(context).selectTime(context, isEffective: true); - }, - child: Text(BlocProvider.of(context).effectiveTime), - )) + BlocProvider.of(context) + .selectTime(context, isEffective: true); + }, + child: Text( + BlocProvider.of(context) + .effectiveTime), + )), ), const Divider(), ListTile( @@ -148,9 +163,13 @@ class CreateTemporaryPassword extends StatelessWidget { width: MediaQuery.of(context).size.width / 2, child: InkWell( onTap: () { - BlocProvider.of(context).selectTime(context, isEffective: false); + BlocProvider.of(context) + .selectTime(context, + isEffective: false); }, - child: Text(BlocProvider.of(context).expirationTime), + child: Text( + BlocProvider.of(context) + .expirationTime), )), ), ), @@ -174,7 +193,8 @@ class CreateTemporaryPassword extends StatelessWidget { value: BlocProvider.of(context).repeat, onChanged: (value) { - BlocProvider.of(context).repeatFunction(); + BlocProvider.of(context) + .repeatFunction(); }, applyTheme: true, )), @@ -185,74 +205,73 @@ class CreateTemporaryPassword extends StatelessWidget { ), BlocProvider.of(context).repeat ? DefaultContainer( - padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5), + padding: const EdgeInsets.symmetric( + horizontal: 5, vertical: 5), child: Column( children: [ Padding( padding: const EdgeInsets.all(8.0), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + 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, - ), + 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, - ), + }, + child: BodyMedium(text: 'End',fontColor:BlocProvider.of(context).isStartEndTime? Colors.black:Colors.blue,fontSize: 18,), ) ], ), ), const Divider(), - SizedBox( + Container( 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); - }, - ), + child: BlocProvider.of(context).isStartEndTime? + Container( + height: 90, + child: 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) { + setState(() { + BlocProvider.of(context).endTime=time; + BlocProvider.of(context).changeTime(time, BlocProvider.of(context).isStartEndTime); + + // dateTime = time; + }); + }, + ), ), const Divider(), const SizedBox(height: 20), @@ -287,4 +306,3 @@ class CreateTemporaryPassword extends StatelessWidget { })); } } - From e8184c00ff0f98f09e6ff8c34288d62b713d1be3 Mon Sep 17 00:00:00 2001 From: mohammad Date: Sat, 29 Jun 2024 23:38:52 +0300 Subject: [PATCH 4/8] CreateTemporaryPassword --- .../bloc/smart_door_bloc/smart_door_bloc.dart | 294 ++++++++++-------- .../smart_door_bloc/smart_door_event.dart | 5 + .../smart_door_bloc/smart_door_state.dart | 5 + .../create_temporary_password_model.dart | 68 ++++ .../model/temporary_password_model.dart | 37 +++ .../ceiling_sensor_interface.dart | 3 +- .../smart_door/create_temporary_password.dart | 85 +++-- .../view/widgets/smart_door/door_dialog.dart | 135 ++++++++ .../view/widgets/smart_door/door_grid.dart | 18 +- .../widgets/smart_door/door_interface.dart | 2 +- .../smart_door/temporary_password_page.dart | 132 +------- .../smart_door/view_temporary_password.dart | 131 ++++++-- lib/services/api/api_links_endpoints.dart | 20 ++ lib/services/api/devices_api.dart | 87 +++++- 14 files changed, 680 insertions(+), 342 deletions(-) create mode 100644 lib/features/devices/model/create_temporary_password_model.dart create mode 100644 lib/features/devices/model/temporary_password_model.dart create mode 100644 lib/features/devices/view/widgets/smart_door/door_dialog.dart 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 29d8a80..01f4035 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,68 +1,30 @@ 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:intl/intl.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'; import 'package:syncrow_app/features/devices/model/device_control_model.dart'; import 'package:syncrow_app/features/devices/model/smart_door_model.dart'; import 'package:syncrow_app/features/devices/model/status_model.dart'; +import 'package:syncrow_app/features/devices/model/create_temporary_password_model.dart'; +import 'package:syncrow_app/features/devices/model/temporary_password_model.dart'; 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 ; - - - changeTime(val,isStartEndTime){ - emit(LoadingInitialState()); - if(isStartEndTime==true){ - startTime=val; - }else{ - endTime=val; - } - emit(changeTimeState()); - } - - bool repeatFunction() { - emit(LoadingInitialState()); - repeat = !repeat; - emit(IsRepeatState()); - return repeat; - } - - - bool isStartEndTimeFun(val) { - emit(LoadingInitialState()); - isStartEndTime = val; - emit(IsStartEndState()); - return isStartEndTime; - } - - + static String pageType = ''; SmartDoorBloc({required this.deviceId}) : super(InitialState()) { on(_fetchSmartDoorStatus); + on(getTemporaryPasswords); on(_updateLock); } - - void _fetchSmartDoorStatus( - InitialEvent event, Emitter emit) async { - emit(LoadingInitialState()); + void _fetchSmartDoorStatus(InitialEvent event, Emitter emit) async { try { + emit(LoadingInitialState()); var response = await DevicesAPI.getDeviceStatus(deviceId); List statusModelList = []; for (var status in response['status']) { @@ -76,6 +38,64 @@ class SmartDoorBloc extends Bloc { } } + void getTemporaryPasswords(InitialPasswordsPage event, Emitter emit) async { + try { + emit(LoadingInitialState()); + pageType=event.type!; + var response = await DevicesAPI.getTemporaryPasswords(deviceId,pageType); + if (response is List) { + temporaryPasswords = response.map((item) => TemporaryPassword.fromJson(item)).toList(); + } else if (response is Map && response.containsKey('data')) { + temporaryPasswords = (response['data'] as List).map((item) => TemporaryPassword.fromJson(item)).toList(); + } else { + throw Exception("Unexpected response format"); + } + emit(TemporaryPasswordsLoadedState( temporaryPassword: temporaryPasswords!)); + } catch (e) { + print(e); + emit(FailedState(errorMessage: e.toString())); + } + } + + 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; + List? temporaryPasswords=[]; + List? oneTimePasswords=[]; + + changeTime(val, isStartEndTime) { + emit(LoadingInitialState()); + if (isStartEndTime == true) { + startTime = val; + } else { + endTime = val; + } + emit(changeTimeState()); + } + + bool toggleRepeat() { + emit(LoadingInitialState()); + repeat = !repeat; + emit(IsRepeatState()); + return repeat; + } + + bool setStartEndTime(bool val) { + emit(LoadingInitialState()); + isStartEndTime = val; + emit(IsStartEndState()); + return isStartEndTime; + } + + void _updateLock(UpdateLockEvent event, Emitter emit) async { emit(LoadingNewSate(smartDoorModel: deviceStatus)); try { @@ -97,8 +117,8 @@ class SmartDoorBloc extends Bloc { emit(LoadingInitialState()); passwordController.clear(); Random random = Random(); - int min = 1000000; // Minimum 7-digit number - int max = 9999999; // Maximum 7-digit number + int min = 1000000; + int max = 9999999; passwordController.text = (min + random.nextInt(max - min + 1)).toString(); emit(GeneratePasswordState()); } @@ -124,34 +144,18 @@ class SmartDoorBloc extends Bloc { 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.'), - ), - ); + if (expirationTimeTimeStamp != null && selectedDateTime.millisecondsSinceEpoch > expirationTimeTimeStamp!) { + _showSnackBar(context, 'Effective Time cannot be later than Expiration Time.'); } else { effectiveTime = selectedDateTime.toString(); - effectiveTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; + 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.'), - ), - ); + if (effectiveTimeTimeStamp != null && selectedDateTime.millisecondsSinceEpoch < effectiveTimeTimeStamp!) { + _showSnackBar(context, 'Expiration Time cannot be earlier than Effective Time.'); } else { expirationTime = selectedDateTime.toString(); - expirationTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; + expirationTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; } } emit(TimeSelectedState()); @@ -160,74 +164,98 @@ class SmartDoorBloc extends Bloc { } - Future saveFunction(BuildContext context,) async { - if (passwordController.text.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Password name required'), - backgroundColor: Colors.red, - ), + Future savePassword(BuildContext context) async { + if (_validateInputs(context)) return; + try { + emit(LoadingInitialState()); + var response = await DevicesAPI.createPassword( + pageType: pageType, + deviceId: deviceId, + effectiveTime: effectiveTimeTimeStamp.toString(), + invalidTime: expirationTimeTimeStamp.toString(), + name: passwordNameController.text, + password: passwordController.text, + scheduleList: [ + if (repeat) + Schedule( + effectiveTime: getTimeOnly(startTime), + invalidTime: getTimeOnly(endTime).toString(), + workingDay: selectedDay!, + ), + ], ); - 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; + add(InitialPasswordsPage(type: pageType)); + emit(TemporaryPasswordsLoadedState(temporaryPassword: temporaryPasswords!)); + Navigator.pop(context); + Navigator.pop(context); + } catch (e) { + emit(FailedState(errorMessage: 'Failed to save password: $e')); } } + + + void deletePassword(BuildContext context, passwordId) async { + try { + emit(LoadingInitialState()); + var response = await DevicesAPI.deletePassword( + deviceId: deviceId, + passwordId: passwordId + ).then((value) async { + add(InitialPasswordsPage(type: pageType)); + }); + } catch (e) { + emit(FailedState(errorMessage: e.toString())); + } + } + + + bool _validateInputs(BuildContext context) { + + if (passwordController.text.isEmpty) { + _showSnackBar(context, 'Password required'); + return true; + } + if (passwordNameController.text.isEmpty) { + _showSnackBar(context, 'Password name required'); + return true; + } + if (effectiveTime == 'Select Time' || effectiveTimeTimeStamp == null) { + _showSnackBar(context, 'Select effective time'); + return true; + } + if (expirationTime == 'Select Time' || expirationTimeTimeStamp == null) { + _showSnackBar(context, 'Select expiration time'); + return true; + } + if(repeat==true&&(endTime==null||startTime==null||selectedDay==null)){ + _showSnackBar(context, 'Start Time and End time and the days required '); + return true; + } + return false; + } + + void _showSnackBar(BuildContext context, String message) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message), + backgroundColor: Colors.red, + ), + ); + } + 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', - ), + DayInWeek("Sun", dayKey: 'Sun'), + DayInWeek("Mon", dayKey: 'Mon'), + DayInWeek("Tue", dayKey: 'Tue'), + DayInWeek("Wed", dayKey: 'Wed'), + DayInWeek("Thu", dayKey: 'Thu'), + DayInWeek("Fri", dayKey: 'Fri'), + DayInWeek("Sat", dayKey: 'Sat'), ]; + String getTimeOnly(DateTime? dateTime) { + if (dateTime == null) return ''; + return DateFormat('HH:mm').format(dateTime); + } + } diff --git a/lib/features/devices/bloc/smart_door_bloc/smart_door_event.dart b/lib/features/devices/bloc/smart_door_bloc/smart_door_event.dart index 146931e..0a481ab 100644 --- a/lib/features/devices/bloc/smart_door_bloc/smart_door_event.dart +++ b/lib/features/devices/bloc/smart_door_bloc/smart_door_event.dart @@ -8,6 +8,11 @@ abstract class SmartDoorEvent extends Equatable { } class InitialEvent extends SmartDoorEvent {} +class InitialPasswordsPage extends SmartDoorEvent { + final String? type; + const InitialPasswordsPage({ this.type}); +} +class InitialOneTimePassword extends SmartDoorEvent {} class UpdateLockEvent extends SmartDoorEvent { final bool value; 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 447ab9a..a672a06 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 @@ -1,5 +1,6 @@ import 'package:equatable/equatable.dart'; import 'package:syncrow_app/features/devices/model/smart_door_model.dart'; +import 'package:syncrow_app/features/devices/model/temporary_password_model.dart'; class SmartDoorState extends Equatable { const SmartDoorState(); @@ -44,3 +45,7 @@ class IsStartEndState extends SmartDoorState{} class changeStartTimeState extends SmartDoorState{} class changeEndTimeState extends SmartDoorState{} class changeTimeState extends SmartDoorState{} +class TemporaryPasswordsLoadedState extends SmartDoorState{ + final List temporaryPassword; + const TemporaryPasswordsLoadedState({required this.temporaryPassword}); +} diff --git a/lib/features/devices/model/create_temporary_password_model.dart b/lib/features/devices/model/create_temporary_password_model.dart new file mode 100644 index 0000000..fe94079 --- /dev/null +++ b/lib/features/devices/model/create_temporary_password_model.dart @@ -0,0 +1,68 @@ + + + +class CreateTemporaryPasswordModel{ + final String name; + late final String password; + late final String effectiveTime; + late final String invalidTime; + List? scheduleList; + + CreateTemporaryPasswordModel({ + required this.name, + required this.password, + required this.effectiveTime, + required this.invalidTime, + this.scheduleList, + }); + + factory CreateTemporaryPasswordModel.fromJson(Map json) { + return CreateTemporaryPasswordModel( + name: json['name'], + password: json['password'], + effectiveTime: json['effectiveTime'], + invalidTime: json['invalidTime'], + scheduleList: (json['scheduleList'] as List) + .map((i) => Schedule.fromJson(i)) + .toList(), + ); + } + + Map toJson() { + return { + 'name': name, + 'password': password, + 'effectiveTime': effectiveTime, + 'invalidTime': invalidTime, + 'scheduleList': scheduleList!.map((i) => i.toJson()).toList(), + }; + } +} + +class Schedule { + final String effectiveTime; + final String invalidTime; + final List workingDay; + + Schedule({ + required this.effectiveTime, + required this.invalidTime, + required this.workingDay, + }); + + factory Schedule.fromJson(Map json) { + return Schedule( + effectiveTime: json['effectiveTime'], + invalidTime: json['invalidTime'], + workingDay: List.from(json['workingDay']), + ); + } + + Map toJson() { + return { + 'effectiveTime': effectiveTime, + 'invalidTime': invalidTime, + 'workingDay': workingDay, + }; + } +} diff --git a/lib/features/devices/model/temporary_password_model.dart b/lib/features/devices/model/temporary_password_model.dart new file mode 100644 index 0000000..40cda2c --- /dev/null +++ b/lib/features/devices/model/temporary_password_model.dart @@ -0,0 +1,37 @@ +class TemporaryPassword { + final int effectiveTime; + final int id; + final int invalidTime; + final String name; + final int phase; + final String phone; + final int sn; + final String timeZone; + final int type; + + TemporaryPassword({ + required this.effectiveTime, + required this.id, + required this.invalidTime, + required this.name, + required this.phase, + required this.phone, + required this.sn, + required this.timeZone, + required this.type, + }); + + factory TemporaryPassword.fromJson(Map json) { + return TemporaryPassword( + effectiveTime: json['effectiveTime'], + id: json['id'], + invalidTime: json['invalidTime'], + name: json['name'], + phase: json['phase'], + phone: json['phone'] ?? '', + sn: json['sn'], + timeZone: json['timeZone'] ?? '', + type: json['type'], + ); + } +} diff --git a/lib/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart b/lib/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart index 5b2db52..bfc050f 100644 --- a/lib/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart +++ b/lib/features/devices/view/widgets/ceiling_sensor/ceiling_sensor_interface.dart @@ -240,8 +240,7 @@ class CeilingSensorInterface extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 20), onTap: () async { - if (ceilingSensorButtons()[index]['title'] == - 'Sensitivity') { + if (ceilingSensorButtons()[index]['title'] == 'Sensitivity') { final result = await showDialog( context: context, builder: (context) { 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 93856b2..4c20bfd 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 @@ -11,21 +11,18 @@ import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dar import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'package:time_picker_spinner/time_picker_spinner.dart'; -class CreateTemporaryPassword extends StatefulWidget { - const CreateTemporaryPassword({super.key}); +class CreateTemporaryPassword extends StatelessWidget { + final String? deviceId; - @override - State createState() => - _CreateTemporaryPasswordState(); -} + const CreateTemporaryPassword({super.key,this.deviceId}); -class _CreateTemporaryPasswordState extends State { @override Widget build(BuildContext context) { return BlocProvider( - create: (BuildContext context) => SmartDoorBloc(deviceId: ''), + create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!), child: BlocConsumer( listener: (context, state) { + print(state); if (state is FailedState) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -34,18 +31,22 @@ class _CreateTemporaryPasswordState extends State { ), ); } - }, builder: (context, state) { + + + }, builder: (context, state) { return DefaultScaffold( title: 'Create Password', actions: [ TextButton( onPressed: () { - BlocProvider.of(context).saveFunction(context); + BlocProvider.of(context).savePassword(context); }, child: const Text('Save')) ], - child: SingleChildScrollView( - child: Column( + child: state is LoadingInitialState? + const Center(child: CircularProgressIndicator()): SingleChildScrollView( + child: + Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -64,8 +65,7 @@ class _CreateTemporaryPasswordState extends State { fontColor: ColorsManager.grayColor, ), DefaultContainer( - padding: - const EdgeInsets.symmetric(horizontal: 5, vertical: 5), + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15), child: Row( children: [ Expanded( @@ -125,8 +125,7 @@ class _CreateTemporaryPasswordState extends State { width: MediaQuery.of(context).size.width / 2, child: TextFormField( controller: - BlocProvider.of(context) - .passwordNameController, + BlocProvider.of(context).passwordNameController, decoration: const InputDecoration( labelText: 'Enter The Name'), )), @@ -146,8 +145,8 @@ class _CreateTemporaryPasswordState extends State { .selectTime(context, isEffective: true); }, child: Text( - BlocProvider.of(context) - .effectiveTime), + BlocProvider.of(context).effectiveTime + ), )), ), const Divider(), @@ -194,7 +193,7 @@ class _CreateTemporaryPasswordState extends State { BlocProvider.of(context).repeat, onChanged: (value) { BlocProvider.of(context) - .repeatFunction(); + .toggleRepeat(); }, applyTheme: true, )), @@ -217,14 +216,14 @@ class _CreateTemporaryPasswordState extends State { children: [ InkWell( onTap: () { - BlocProvider.of(context).isStartEndTimeFun(true); + BlocProvider.of(context).setStartEndTime(true); }, child: BodyMedium(text: 'Start',fontColor:BlocProvider.of(context).isStartEndTime==false? Colors.black:Colors.blue,fontSize: 18,), ), InkWell( onTap: () { - BlocProvider.of(context).isStartEndTimeFun(false); + BlocProvider.of(context).setStartEndTime(false); }, child: BodyMedium(text: 'End',fontColor:BlocProvider.of(context).isStartEndTime? Colors.black:Colors.blue,fontSize: 18,), ) @@ -234,10 +233,10 @@ class _CreateTemporaryPasswordState extends State { const Divider(), Container( height: 80, - child: BlocProvider.of(context).isStartEndTime? + child: Container( height: 90, - child: TimePickerSpinner( + child:BlocProvider.of(context).isStartEndTime? TimePickerSpinner( time: BlocProvider.of(context).startTime, is24HourMode: false, @@ -248,30 +247,24 @@ class _CreateTemporaryPasswordState extends State { highlightedTextStyle: const TextStyle( fontSize: 30, color: Colors.blue), onTimeChange: (time) { - BlocProvider.of(context).startTime=time; BlocProvider.of(context).changeTime(time, BlocProvider.of(context).isStartEndTime); - }, + ): Container( + child: TimePickerSpinner( + time: BlocProvider.of(context).endTime, + is24HourMode: false, + itemHeight: 40, + normalTextStyle: const TextStyle( + fontSize: 24, + ), + highlightedTextStyle: const TextStyle( + fontSize: 30, color: Colors.blue), + onTimeChange: (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) { - setState(() { - BlocProvider.of(context).endTime=time; - BlocProvider.of(context).changeTime(time, BlocProvider.of(context).isStartEndTime); - - // dateTime = time; - }); - }, - ), + ) ), const Divider(), const SizedBox(height: 20), @@ -288,9 +281,7 @@ class _CreateTemporaryPasswordState extends State { borderRadius: BorderRadius.circular(30.0), color: Colors.white), onSelect: (values) { - print(values); - BlocProvider.of(context) - .selectedDay = values; + BlocProvider.of(context).selectedDay = values; }, ), ], diff --git a/lib/features/devices/view/widgets/smart_door/door_dialog.dart b/lib/features/devices/view/widgets/smart_door/door_dialog.dart new file mode 100644 index 0000000..022a20e --- /dev/null +++ b/lib/features/devices/view/widgets/smart_door/door_dialog.dart @@ -0,0 +1,135 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:syncrow_app/features/devices/model/device_model.dart'; +import 'package:syncrow_app/features/devices/model/temporary_password_model.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/helpers/snack_bar.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; + +class DoorDialog extends StatefulWidget { + final String title; + final TemporaryPassword value; + + const DoorDialog({ + super.key, + required this.title, + required this.value, + }); + + @override + DoorDialogState createState() => DoorDialogState(); +} + +class DoorDialogState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + final DateTime effectiveDateTime = + DateTime.fromMillisecondsSinceEpoch(widget.value.effectiveTime); + final DateTime expiredDateTime = + DateTime.fromMillisecondsSinceEpoch(widget.value.invalidTime); + final DateFormat formatter = DateFormat('HH:mm'); + return Dialog( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + ), + padding: const EdgeInsets.only(top: 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + BodyMedium( + text: widget.title, + style: context.bodyMedium.copyWith( + color: ColorsManager.primaryColorWithOpacity, + fontWeight: FontsManager.extraBold, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 15, + horizontal: 50, + ), + child: Container( + height: 1, + width: double.infinity, + color: ColorsManager.greyColor, + ), + ), + Padding( + padding: const EdgeInsets.all(15.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('Effective Time :'), + Text(formatter.format(effectiveDateTime)), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('Expired Time :'), + Text(formatter.format(expiredDateTime)), + ], + ), + ], + ), + ), + Container( + height: 1, + width: double.infinity, + color: ColorsManager.greyColor, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + InkWell( + onTap: () { + Navigator.pop(context); + }, + child: Center( + child: BodyMedium( + text: 'Cancel', + style: context.bodyMedium + .copyWith(color: ColorsManager.greyColor), + ), + ), + ), + Container( + height: 50, + width: 1, + color: ColorsManager.greyColor, + ), + InkWell( + onTap: () { + Navigator.pop(context, 'delete'); + + }, + child: Center( + child: BodyMedium( + text: 'Delete Password', + style: context.bodyMedium.copyWith( + color: ColorsManager.primaryColorWithOpacity), + ), + ), + ), + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/features/devices/view/widgets/smart_door/door_grid.dart b/lib/features/devices/view/widgets/smart_door/door_grid.dart index d3542b3..fc5c290 100644 --- a/lib/features/devices/view/widgets/smart_door/door_grid.dart +++ b/lib/features/devices/view/widgets/smart_door/door_grid.dart @@ -10,9 +10,9 @@ import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart import 'package:syncrow_app/generated/assets.dart'; class DoorLockGrid extends StatelessWidget { - const DoorLockGrid({ - super.key, - }); + String uuid; + DoorLockGrid({ + super.key,required this.uuid}); @override Widget build(BuildContext context) { @@ -29,10 +29,10 @@ class DoorLockGrid extends StatelessWidget { itemBuilder: (context, index) => DefaultContainer( onTap: () { //TODO: remove checking after adding the pages - doorLockButtons[index]['page'] != null + doorLockButtons()[index]['page'] != null ? Navigator.of(context).push( MaterialPageRoute( - builder: (context) => doorLockButtons[index]['page'] as Widget, + builder: (context) => doorLockButtons(val: uuid)[index]['page'] as Widget, ), ) : null; @@ -44,7 +44,7 @@ class DoorLockGrid extends StatelessWidget { ConstrainedBox( constraints: const BoxConstraints(maxHeight: 46, maxWidth: 50), child: SvgPicture.asset( - doorLockButtons[index]['image'] as String, + doorLockButtons()[index]['image'] as String, ), ), const SizedBox( @@ -53,7 +53,7 @@ class DoorLockGrid extends StatelessWidget { Flexible( child: FittedBox( child: BodySmall( - text: doorLockButtons[index]['title'] as String, + text: doorLockButtons()[index]['title'] as String, // doorLockButtons.keys.elementAt(index), textAlign: TextAlign.center, ), @@ -66,7 +66,7 @@ class DoorLockGrid extends StatelessWidget { } } -List> doorLockButtons = [ +List> doorLockButtons({val}) => [ { 'title': 'Unlocking Records', 'image': Assets.assetsIconsDoorlockAssetsUnlockingRecords, @@ -80,7 +80,7 @@ List> doorLockButtons = [ { 'title': 'Temporary Password', 'image': Assets.assetsIconsDoorlockAssetsTemporaryPassword, - 'page':const TemporaryPasswordPage() , + 'page': TemporaryPasswordPage(deviceId:val) , }, { 'title': 'Smart Linkage', diff --git a/lib/features/devices/view/widgets/smart_door/door_interface.dart b/lib/features/devices/view/widgets/smart_door/door_interface.dart index 2d9620f..b78378f 100644 --- a/lib/features/devices/view/widgets/smart_door/door_interface.dart +++ b/lib/features/devices/view/widgets/smart_door/door_interface.dart @@ -111,7 +111,7 @@ class DoorInterface extends StatelessWidget { doorLock: doorLock, smartDoorModel: smartDoorModel, ), - const DoorLockGrid(), + DoorLockGrid( uuid: doorLock.uuid!, ), ], ) diff --git a/lib/features/devices/view/widgets/smart_door/temporary_password_page.dart b/lib/features/devices/view/widgets/smart_door/temporary_password_page.dart index 4cf1920..7efadb7 100644 --- a/lib/features/devices/view/widgets/smart_door/temporary_password_page.dart +++ b/lib/features/devices/view/widgets/smart_door/temporary_password_page.dart @@ -9,14 +9,10 @@ import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dar import 'package:syncrow_app/generated/assets.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; -class TemporaryPasswordPage extends StatefulWidget { - const TemporaryPasswordPage({super.key}); +class TemporaryPasswordPage extends StatelessWidget { + final String? deviceId; + const TemporaryPasswordPage({super.key,this.deviceId}); - @override - State createState() => _TemporaryPasswordPageState(); -} - -class _TemporaryPasswordPageState extends State { @override Widget build(BuildContext context) { return DefaultScaffold( @@ -50,48 +46,15 @@ class _TemporaryPasswordPageState extends State { fontWeight: FontWeight.normal, ), onTap: () { - Navigator.of(context).push(MaterialPageRoute(builder: (context) => const ViewTemporaryPassword(),)); + Navigator.of(context).push(MaterialPageRoute(builder: (context) => ViewTemporaryPassword(deviceId:deviceId,type:'Online Password'),)); }, trailing: const Icon( + Icons.arrow_forward_ios, color: ColorsManager.greyColor, size: 15, ), ), - // Row( - // children: [ - // Expanded( - // child: Row( - // children: [ - // SizedBox( - // width: 45, - // height: 40, - // child: SvgPicture.asset( - // Assets.timeLimitedPasswordIcon), - // ), - // const SizedBox(width: 15), - // const Expanded( - // child: - // BodyMedium( - // text: 'Time-Limited Password', - // fontWeight: FontWeight.normal, - // ), - // ), - // ], - // ), - // ), - // const Row( - // mainAxisAlignment: MainAxisAlignment.end, - // children: [ - // Icon( - // Icons.arrow_forward_ios, - // color: ColorsManager.greyColor, - // size: 15, - // ), - // ], - // ), - // ], - // ), ), ], ), @@ -123,96 +86,35 @@ class _TemporaryPasswordPageState extends State { text: 'One-Time Password', fontWeight: FontWeight.normal, ), - onTap: () {}, + onTap: () { + Navigator.of(context).push(MaterialPageRoute(builder: (context) => ViewTemporaryPassword(deviceId:deviceId,type:'One-Time'),)); + + }, trailing: const Icon( Icons.arrow_forward_ios, color: ColorsManager.greyColor, size: 15, ), ), - // Row( - // children: [ - // Expanded( - // child: Row( - // children: [ - // SizedBox( - // width: 45, - // height: 40, - // child: SvgPicture.asset( - // Assets.oneTimePassword), - // ), - // const SizedBox(width: 15), - // const Expanded( - // child: BodyMedium( - // text: 'One-Time Password', - // fontWeight: FontWeight.normal, - // ), - // ), - // ], - // ), - // ), - // const Row( - // mainAxisAlignment: MainAxisAlignment.end, - // children: [ - // Icon( - // Icons.arrow_forward_ios, - // color: ColorsManager.greyColor, - // size: 15, - // ), - // ], - // ), - // ], - // ), + const Divider( ), ListTile( contentPadding: EdgeInsets.zero, leading: SvgPicture.asset( Assets.timeLimitedPassword), - title: BodyMedium( - text: 'Time-Limited Password', - fontWeight: FontWeight.normal, - ), - onTap: () {}, + title: const BodyMedium( + text: 'Time-Limited Password', + fontWeight: FontWeight.normal, + ), + onTap: () { + Navigator.of(context).push(MaterialPageRoute(builder: (context) => ViewTemporaryPassword(deviceId:deviceId,type:'Time-Limited'),)); + }, trailing: const Icon( Icons.arrow_forward_ios, color: ColorsManager.greyColor, size: 15, ), ), - // Row( - // children: [ - // Expanded( - // child: Row( - // children: [ - // SizedBox( - // width: 45, - // height: 40, - // child: SvgPicture.asset( - // Assets.timeLimitedPassword), - // - // ), - // const SizedBox(width: 15), - // const Expanded( - // child: BodyMedium( - // text: 'Time-Limited Password', - // fontWeight: FontWeight.normal, - // ), - // ), - // ], - // ), - // ), - // const Row( - // mainAxisAlignment: MainAxisAlignment.end, - // children: [ - // Icon( - // Icons.arrow_forward_ios, - // color: ColorsManager.greyColor, - // size: 15, - // ), - // ], - // ), - // ], - // ), ],) ), ], diff --git a/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart b/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart index b65641d..8dede56 100644 --- a/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart +++ b/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart @@ -1,45 +1,112 @@ - import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.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_event.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'; -import 'package:syncrow_app/features/shared_widgets/text_widgets/body_small.dart'; import 'package:syncrow_app/generated/assets.dart'; - +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; import 'create_temporary_password.dart'; - +import 'door_dialog.dart'; class ViewTemporaryPassword extends StatelessWidget { - const ViewTemporaryPassword({super.key}); + final String? deviceId; + final String? type; + const ViewTemporaryPassword({super.key, this.deviceId, this.type}); @override Widget build(BuildContext context) { - return DefaultScaffold( - title: 'Passwords', - actions: [ - IconButton( - onPressed: () { - Navigator.of(context).push(MaterialPageRoute(builder: (context) => const CreateTemporaryPassword(),)); - - }, - icon: const Icon(Icons.add) - ) - ], - child: Container( - child: Center( - child:Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SvgPicture.asset( - Assets.noValidPasswords - ), - const SizedBox(height: 10,), - const BodyMedium(text: 'No Valid Passwords') - ], - ), - ), - ) - );; + return BlocProvider( + create: (BuildContext context) => + SmartDoorBloc(deviceId: deviceId!)..add(InitialPasswordsPage(type:type )), + 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: 'Passwords', + actions: [ + IconButton( + onPressed: () { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => + CreateTemporaryPassword(deviceId: deviceId), + )); + }, + icon: const Icon(Icons.add)) + ], + child: Builder( + builder: (context) { + final smartDoorBloc = BlocProvider.of(context); + return state is LoadingInitialState + ? const Center(child: CircularProgressIndicator()) + : Center( + child: smartDoorBloc.temporaryPasswords!.isNotEmpty + ? ListView.builder( + itemCount: smartDoorBloc.temporaryPasswords!.length, + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.all(5.0), + child: DefaultContainer( + padding: const EdgeInsets.symmetric( + horizontal: 15, vertical: 10), + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: SvgPicture.asset( + Assets.timeLimitedPasswordIcon), + title: BodyMedium( + text: + 'Password Name: ${smartDoorBloc.temporaryPasswords![index].name}', + fontWeight: FontWeight.normal, + ), + onTap: () async { + final result = await showDialog( + context: context, + builder: (context) { + return DoorDialog( + title: 'Password Information', + value: smartDoorBloc.temporaryPasswords![index], + ); + }, + ); + if(result=='delete'){ + smartDoorBloc.deletePassword(context, smartDoorBloc.temporaryPasswords![index].id.toString()); + } + }, + trailing: const Icon( + Icons.arrow_forward_ios, + color: ColorsManager.greyColor, + size: 15, + ), + ), + ), + ); + }, + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset(Assets.noValidPasswords), + const SizedBox( + height: 10, + ), + const BodyMedium(text: 'No Valid Passwords') + ], + ), + ); + }, + )); + })); } } diff --git a/lib/services/api/api_links_endpoints.dart b/lib/services/api/api_links_endpoints.dart index 3f6476d..9c50dc6 100644 --- a/lib/services/api/api_links_endpoints.dart +++ b/lib/services/api/api_links_endpoints.dart @@ -111,4 +111,24 @@ abstract class ApiEndpoints { static const String editDevicePermission = '$baseUrl/device-permission/edit/{userId}'; static const String assignDeviceToRoom = '$baseUrl/device/room'; + + + //////////////////////Door Lock ////////////////////// + //online + static const String addTemporaryPassword = '$baseUrl/door-lock/temporary-password/online/{doorLockUuid}'; + static const String getTemporaryPassword = '$baseUrl/door-lock/temporary-password/online/{doorLockUuid}'; + + + //one-time offline + static const String addOneTimeTemporaryPassword = '$baseUrl/door-lock/temporary-password/offline/one-time/{doorLockUuid}'; + static const String getOneTimeTemporaryPassword = '$baseUrl/door-lock/temporary-password/offline/one-time/{doorLockUuid}'; + + //multiple-time offline + static const String addMultipleTimeTemporaryPassword = '$baseUrl/door-lock/temporary-password/offline/multiple-time/{doorLockUuid}'; + static const String getMultipleTimeTemporaryPassword = '$baseUrl/door-lock/temporary-password/offline/multiple-time/{doorLockUuid}'; + + //multiple-time offline + static const String deleteTemporaryPassword = '$baseUrl/door-lock/temporary-password/{doorLockUuid}/{passwordId}'; + + } diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index aebbe94..a9a7b82 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'package:syncrow_app/features/devices/model/device_category_model.dart'; import 'package:syncrow_app/features/devices/model/device_control_model.dart'; @@ -6,6 +7,8 @@ import 'package:syncrow_app/features/devices/model/device_model.dart'; import 'package:syncrow_app/services/api/api_links_endpoints.dart'; import 'package:syncrow_app/services/api/http_service.dart'; +import '../../features/devices/model/create_temporary_password_model.dart'; + class DevicesAPI { static final HTTPService _httpService = HTTPService(); @@ -58,7 +61,8 @@ class DevicesAPI { static Future> getDeviceStatus(String deviceId) async { final response = await _httpService.get( - path: ApiEndpoints.deviceFunctionsStatus.replaceAll('{deviceUuid}', deviceId), + path: ApiEndpoints.deviceFunctionsStatus + .replaceAll('{deviceUuid}', deviceId), showServerMessage: false, expectedResponseModel: (json) { return json; @@ -67,7 +71,8 @@ class DevicesAPI { return response; } - static Future> getDeviceByGroupName(String unitId, String groupName) async { + static Future> getDeviceByGroupName( + String unitId, String groupName) async { final response = await _httpService.get( path: ApiEndpoints.devicesByGroupName .replaceAll('{unitUuid}', unitId) @@ -106,7 +111,8 @@ class DevicesAPI { return response; } - static Future> getDevicesByGatewayId(String gatewayId) async { + static Future> getDevicesByGatewayId( + String gatewayId) async { final response = await _httpService.get( path: ApiEndpoints.gatewayApi.replaceAll('{gatewayUuid}', gatewayId), showServerMessage: false, @@ -123,4 +129,79 @@ class DevicesAPI { ); return response; } + + static Future getTemporaryPasswords(String deviceId, pageType) async { + final response = await _httpService.get( + path: pageType == 'One-Time' + ? ApiEndpoints.getOneTimeTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId) + : pageType == 'Online Password' + ? ApiEndpoints.getTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId) + : ApiEndpoints.getMultipleTimeTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId), + showServerMessage: false, + expectedResponseModel: (json) { + return json; + }, + ); + return response; + } + +//create password + static Future createPassword({ + required String name, + required String password, + required String effectiveTime, + required String invalidTime, + required String deviceId, + required String pageType, + List? scheduleList, + }) async { + try { + String endpointPath; + if (pageType == 'Online Password') { + endpointPath = ApiEndpoints.addTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId); + } else if (pageType == 'One-Time') { + endpointPath = ApiEndpoints.addOneTimeTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId); + } else { + endpointPath = ApiEndpoints.addMultipleTimeTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId); + } + Map body = { + "name": name, + "password": password, + "effectiveTime": effectiveTime, + "invalidTime": invalidTime, + }; + if (pageType == 'Online Password' && scheduleList != null) { + body["scheduleList"] = + scheduleList.map((schedule) => schedule.toJson()).toList(); + } + final response = await _httpService.post( + path: endpointPath, + body: body, + showServerMessage: false, + expectedResponseModel: (json) => json, + ); + return response; + } catch (e) {} + } + + + static Future> deletePassword( + {required String passwordId, required String deviceId}) async { + final response = await _httpService.delete( + path: ApiEndpoints.deleteTemporaryPassword + .replaceAll('{doorLockUuid}', deviceId) + .replaceAll('{passwordId}', passwordId), + showServerMessage: false, + expectedResponseModel: (json) { + return json; + }, + ); + return response; + } } From babaada63faa459553f3fb8dae5a30b67aa6dc21 Mon Sep 17 00:00:00 2001 From: mohammad Date: Sat, 29 Jun 2024 23:44:47 +0300 Subject: [PATCH 5/8] CreateTemporaryPassword --- .../bloc/smart_door_bloc/smart_door_bloc.dart | 14 +++++++------- .../smart_door/create_temporary_password.dart | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) 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 01f4035..35fd6d5 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 @@ -244,13 +244,13 @@ class SmartDoorBloc extends Bloc { } List days = [ - DayInWeek("Sun", dayKey: 'Sun'), - DayInWeek("Mon", dayKey: 'Mon'), - DayInWeek("Tue", dayKey: 'Tue'), - DayInWeek("Wed", dayKey: 'Wed'), - DayInWeek("Thu", dayKey: 'Thu'), - DayInWeek("Fri", dayKey: 'Fri'), - DayInWeek("Sat", dayKey: 'Sat'), + DayInWeek("S", dayKey: 'Sun'), + DayInWeek("M", dayKey: 'Mon'), + DayInWeek("T", dayKey: 'Tue'), + DayInWeek("W", dayKey: 'Wed'), + DayInWeek("T", dayKey: 'Thu'), + DayInWeek("F", dayKey: 'Fri'), + DayInWeek("S", dayKey: 'Sat'), ]; String getTimeOnly(DateTime? dateTime) { 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 4c20bfd..28aa889 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 @@ -112,7 +112,7 @@ class CreateTemporaryPassword extends StatelessWidget { height: 20, ), DefaultContainer( - padding: const EdgeInsets.all(10), + padding: const EdgeInsets.all(20), child: Column( children: [ ListTile( @@ -130,7 +130,7 @@ class CreateTemporaryPassword extends StatelessWidget { labelText: 'Enter The Name'), )), ), - const Divider(), + const Divider(color:Color(0xffEBEBEB),), ListTile( contentPadding: EdgeInsets.zero, leading: const BodyMedium( @@ -149,7 +149,7 @@ class CreateTemporaryPassword extends StatelessWidget { ), )), ), - const Divider(), + const Divider(color:Color(0xffEBEBEB),), ListTile( contentPadding: EdgeInsets.zero, leading: const BodyMedium( @@ -205,7 +205,7 @@ class CreateTemporaryPassword extends StatelessWidget { BlocProvider.of(context).repeat ? DefaultContainer( padding: const EdgeInsets.symmetric( - horizontal: 5, vertical: 5), + horizontal: 20, vertical: 5), child: Column( children: [ Padding( @@ -230,7 +230,7 @@ class CreateTemporaryPassword extends StatelessWidget { ], ), ), - const Divider(), + const Divider(color:Color(0xffEBEBEB),), Container( height: 80, child: @@ -266,11 +266,11 @@ class CreateTemporaryPassword extends StatelessWidget { ), ) ), - const Divider(), + const Divider(color:Color(0xffEBEBEB),), const SizedBox(height: 20), SelectWeekDays( width: MediaQuery.of(context).size.width / 1, - fontSize: 10, + fontSize: 18, fontWeight: FontWeight.w600, days: BlocProvider.of(context) .days, From 578b9586d28d607efd306d66122ccf36f2e585b4 Mon Sep 17 00:00:00 2001 From: mohammad Date: Sun, 30 Jun 2024 11:54:14 +0300 Subject: [PATCH 6/8] CreateTemporaryPassword --- .../bloc/smart_door_bloc/smart_door_bloc.dart | 79 +++++++++++-------- .../smart_door_bloc/smart_door_state.dart | 2 + .../smart_door/create_temporary_password.dart | 45 +++++++---- .../smart_door/view_temporary_password.dart | 13 ++- .../widgets/manage_home/manage_home_view.dart | 3 +- 5 files changed, 87 insertions(+), 55 deletions(-) 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 35fd6d5..b384ad4 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 @@ -3,6 +3,7 @@ import 'package:day_picker/model/day_in_week.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:intl/intl.dart'; +import 'package:pin_code_fields/pin_code_fields.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'; import 'package:syncrow_app/features/devices/model/device_control_model.dart'; @@ -11,6 +12,7 @@ import 'package:syncrow_app/features/devices/model/status_model.dart'; import 'package:syncrow_app/features/devices/model/create_temporary_password_model.dart'; import 'package:syncrow_app/features/devices/model/temporary_password_model.dart'; import 'package:syncrow_app/services/api/devices_api.dart'; +import 'package:syncrow_app/utils/helpers/snack_bar.dart'; class SmartDoorBloc extends Bloc { final String deviceId; @@ -22,7 +24,8 @@ class SmartDoorBloc extends Bloc { on(getTemporaryPasswords); on(_updateLock); } - void _fetchSmartDoorStatus(InitialEvent event, Emitter emit) async { + void _fetchSmartDoorStatus( + InitialEvent event, Emitter emit) async { try { emit(LoadingInitialState()); var response = await DevicesAPI.getDeviceStatus(deviceId); @@ -38,21 +41,25 @@ class SmartDoorBloc extends Bloc { } } - void getTemporaryPasswords(InitialPasswordsPage event, Emitter emit) async { + void getTemporaryPasswords( + InitialPasswordsPage event, Emitter emit) async { try { emit(LoadingInitialState()); - pageType=event.type!; - var response = await DevicesAPI.getTemporaryPasswords(deviceId,pageType); + pageType = event.type!; + var response = await DevicesAPI.getTemporaryPasswords(deviceId, pageType); if (response is List) { - temporaryPasswords = response.map((item) => TemporaryPassword.fromJson(item)).toList(); + temporaryPasswords = + response.map((item) => TemporaryPassword.fromJson(item)).toList(); } else if (response is Map && response.containsKey('data')) { - temporaryPasswords = (response['data'] as List).map((item) => TemporaryPassword.fromJson(item)).toList(); + temporaryPasswords = (response['data'] as List) + .map((item) => TemporaryPassword.fromJson(item)) + .toList(); } else { throw Exception("Unexpected response format"); } - emit(TemporaryPasswordsLoadedState( temporaryPassword: temporaryPasswords!)); + emit(TemporaryPasswordsLoadedState( + temporaryPassword: temporaryPasswords!)); } catch (e) { - print(e); emit(FailedState(errorMessage: e.toString())); } } @@ -61,15 +68,15 @@ class SmartDoorBloc extends Bloc { TextEditingController passwordNameController = TextEditingController(); String effectiveTime = 'Select Time'; int? effectiveTimeTimeStamp; - String expirationTime ='Select Time'; + String expirationTime = 'Select Time'; int? expirationTimeTimeStamp; bool repeat = false; bool isStartEndTime = true; List? selectedDay; DateTime? startTime; DateTime? endTime; - List? temporaryPasswords=[]; - List? oneTimePasswords=[]; + List? temporaryPasswords = []; + List? oneTimePasswords = []; changeTime(val, isStartEndTime) { emit(LoadingInitialState()); @@ -95,7 +102,6 @@ class SmartDoorBloc extends Bloc { return isStartEndTime; } - void _updateLock(UpdateLockEvent event, Emitter emit) async { emit(LoadingNewSate(smartDoorModel: deviceStatus)); try { @@ -123,7 +129,8 @@ class SmartDoorBloc extends Bloc { emit(GeneratePasswordState()); } - Future selectTime(BuildContext context, {required bool isEffective}) async { + Future selectTime(BuildContext context, + {required bool isEffective}) async { final DateTime? picked = await showDatePicker( context: context, initialDate: DateTime.now(), @@ -144,18 +151,24 @@ class SmartDoorBloc extends Bloc { timePicked.minute, ); if (isEffective) { - if (expirationTimeTimeStamp != null && selectedDateTime.millisecondsSinceEpoch > expirationTimeTimeStamp!) { - _showSnackBar(context, 'Effective Time cannot be later than Expiration Time.'); + if (expirationTimeTimeStamp != null && + selectedDateTime.millisecondsSinceEpoch > + expirationTimeTimeStamp!) { + _showSnackBar(context, + 'Effective Time cannot be later than Expiration Time.'); } else { effectiveTime = selectedDateTime.toString(); - effectiveTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; + effectiveTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; } } else { - if (effectiveTimeTimeStamp != null && selectedDateTime.millisecondsSinceEpoch < effectiveTimeTimeStamp!) { - _showSnackBar(context, 'Expiration Time cannot be earlier than Effective Time.'); + if (effectiveTimeTimeStamp != null && + selectedDateTime.millisecondsSinceEpoch < + effectiveTimeTimeStamp!) { + _showSnackBar(context, + 'Expiration Time cannot be earlier than Effective Time.'); } else { expirationTime = selectedDateTime.toString(); - expirationTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; + expirationTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; } } emit(TimeSelectedState()); @@ -163,11 +176,10 @@ class SmartDoorBloc extends Bloc { } } - Future savePassword(BuildContext context) async { if (_validateInputs(context)) return; try { - emit(LoadingInitialState()); + emit(LoadingSaveState()); var response = await DevicesAPI.createPassword( pageType: pageType, deviceId: deviceId, @@ -175,7 +187,7 @@ class SmartDoorBloc extends Bloc { invalidTime: expirationTimeTimeStamp.toString(), name: passwordNameController.text, password: passwordController.text, - scheduleList: [ + scheduleList: [ if (repeat) Schedule( effectiveTime: getTimeOnly(startTime), @@ -183,24 +195,22 @@ class SmartDoorBloc extends Bloc { workingDay: selectedDay!, ), ], - ); - add(InitialPasswordsPage(type: pageType)); - emit(TemporaryPasswordsLoadedState(temporaryPassword: temporaryPasswords!)); - Navigator.pop(context); - Navigator.pop(context); + ).then((value)async { + add(InitialPasswordsPage(type: pageType)); + },); + CustomSnackBar.displaySnackBar('Save Successfully'); + emit(SaveState()); } catch (e) { emit(FailedState(errorMessage: 'Failed to save password: $e')); } } - void deletePassword(BuildContext context, passwordId) async { try { emit(LoadingInitialState()); var response = await DevicesAPI.deletePassword( - deviceId: deviceId, - passwordId: passwordId - ).then((value) async { + deviceId: deviceId, passwordId: passwordId) + .then((value) async { add(InitialPasswordsPage(type: pageType)); }); } catch (e) { @@ -208,9 +218,7 @@ class SmartDoorBloc extends Bloc { } } - bool _validateInputs(BuildContext context) { - if (passwordController.text.isEmpty) { _showSnackBar(context, 'Password required'); return true; @@ -227,7 +235,8 @@ class SmartDoorBloc extends Bloc { _showSnackBar(context, 'Select expiration time'); return true; } - if(repeat==true&&(endTime==null||startTime==null||selectedDay==null)){ + if (repeat == true && + (endTime == null || startTime == null || selectedDay == null)) { _showSnackBar(context, 'Start Time and End time and the days required '); return true; } @@ -246,7 +255,7 @@ class SmartDoorBloc extends Bloc { List days = [ DayInWeek("S", dayKey: 'Sun'), DayInWeek("M", dayKey: 'Mon'), - DayInWeek("T", dayKey: 'Tue'), + DayInWeek("T", dayKey: 'Tue'), DayInWeek("W", dayKey: 'Wed'), DayInWeek("T", dayKey: 'Thu'), DayInWeek("F", dayKey: 'Fri'), 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 a672a06..473a768 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 @@ -45,6 +45,8 @@ class IsStartEndState extends SmartDoorState{} class changeStartTimeState extends SmartDoorState{} class changeEndTimeState extends SmartDoorState{} class changeTimeState extends SmartDoorState{} +class SaveState extends SmartDoorState{} +class LoadingSaveState extends SmartDoorState{} class TemporaryPasswordsLoadedState extends SmartDoorState{ final List temporaryPassword; const TemporaryPasswordsLoadedState({required this.temporaryPassword}); 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 28aa889..c804f7a 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 @@ -4,25 +4,27 @@ 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_event.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_large.dart'; import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; import 'package:time_picker_spinner/time_picker_spinner.dart'; class CreateTemporaryPassword extends StatelessWidget { final String? deviceId; + final String? type; - const CreateTemporaryPassword({super.key,this.deviceId}); - + const CreateTemporaryPassword({super.key,this.deviceId, this.type}); @override Widget build(BuildContext context) { return BlocProvider( create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!), child: BlocConsumer( listener: (context, state) { - print(state); if (state is FailedState) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -31,18 +33,30 @@ class CreateTemporaryPassword extends StatelessWidget { ), ); } - - - }, builder: (context, state) { + }, + builder: (context, state) { return DefaultScaffold( - title: 'Create Password', - actions: [ - TextButton( - onPressed: () { - BlocProvider.of(context).savePassword(context); - }, - child: const Text('Save')) - ], + appBar: AppBar( + backgroundColor: Colors.transparent, + centerTitle: true, + title: const BodyLarge( + text: 'Create Password' , + fontColor: ColorsManager.primaryColor, + fontWeight: FontsManager.bold, + ), + leading: IconButton(onPressed: () { + Navigator.of(context).pop('UpdatePage'); + }, icon: Icon(Icons.arrow_back)), + actions: [ + TextButton( + onPressed: () { + BlocProvider.of(context).savePassword(context); + SmartDoorBloc(deviceId: deviceId!).add(InitialPasswordsPage(type:type )); + }, + child: const Text('Save')) + ], + ), + child: state is LoadingInitialState? const Center(child: CircularProgressIndicator()): SingleChildScrollView( child: @@ -70,7 +84,8 @@ class CreateTemporaryPassword extends StatelessWidget { children: [ Expanded( child: PinCodeTextField( - keyboardType: TextInputType.phone, + autoDisposeControllers: false, + keyboardType: TextInputType.phone, length: 7, enabled: true, obscureText: false, diff --git a/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart b/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart index 8dede56..1675e56 100644 --- a/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart +++ b/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart @@ -24,6 +24,7 @@ class ViewTemporaryPassword extends StatelessWidget { SmartDoorBloc(deviceId: deviceId!)..add(InitialPasswordsPage(type:type )), child: BlocConsumer( listener: (context, state) { + print('=======4${state}'); if (state is FailedState) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -33,21 +34,25 @@ class ViewTemporaryPassword extends StatelessWidget { ); } }, builder: (context, state) { + final smartDoorBloc = BlocProvider.of(context); + return DefaultScaffold( title: 'Passwords', actions: [ IconButton( onPressed: () { Navigator.of(context).push(MaterialPageRoute( - builder: (context) => - CreateTemporaryPassword(deviceId: deviceId), - )); + builder: (context) => CreateTemporaryPassword(deviceId: deviceId, type: type), + )).then((result) { + if (result != null) { + smartDoorBloc.add(InitialPasswordsPage(type:type )); } + }); + }, icon: const Icon(Icons.add)) ], child: Builder( builder: (context) { - final smartDoorBloc = BlocProvider.of(context); return state is LoadingInitialState ? const Center(child: CircularProgressIndicator()) : Center( diff --git a/lib/features/menu/view/widgets/manage_home/manage_home_view.dart b/lib/features/menu/view/widgets/manage_home/manage_home_view.dart index eeb59ce..7704659 100644 --- a/lib/features/menu/view/widgets/manage_home/manage_home_view.dart +++ b/lib/features/menu/view/widgets/manage_home/manage_home_view.dart @@ -30,7 +30,8 @@ class ManageHomeView extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, - children: List.generate( + children: + List.generate( spaces.length, (index) { if (index == spaces.length - 1) { From 695d6c109bbaf2920fff8a32be52ba36ccd24fcb Mon Sep 17 00:00:00 2001 From: mohammad Date: Sun, 30 Jun 2024 12:49:05 +0300 Subject: [PATCH 7/8] CreateTemporaryPassword --- .../bloc/smart_door_bloc/smart_door_bloc.dart | 12 ++++++++---- .../smart_door/create_temporary_password.dart | 9 ++++++++- .../widgets/smart_door/temporary_password_page.dart | 1 - .../widgets/smart_door/view_temporary_password.dart | 6 ++---- 4 files changed, 18 insertions(+), 10 deletions(-) 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 b384ad4..2cae2e9 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 @@ -219,20 +219,24 @@ class SmartDoorBloc extends Bloc { } bool _validateInputs(BuildContext context) { + if (passwordController.text.length<7) { + CustomSnackBar.displaySnackBar('Password less than 7'); + return true; + } if (passwordController.text.isEmpty) { - _showSnackBar(context, 'Password required'); + CustomSnackBar.displaySnackBar('Password required'); return true; } if (passwordNameController.text.isEmpty) { - _showSnackBar(context, 'Password name required'); + CustomSnackBar.displaySnackBar('Password name required'); return true; } if (effectiveTime == 'Select Time' || effectiveTimeTimeStamp == null) { - _showSnackBar(context, 'Select effective time'); + CustomSnackBar.displaySnackBar('Select effective time'); return true; } if (expirationTime == 'Select Time' || expirationTimeTimeStamp == null) { - _showSnackBar(context, 'Select expiration time'); + CustomSnackBar.displaySnackBar('Select expiration time'); return true; } if (repeat == true && 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 c804f7a..6fd500c 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,6 +1,7 @@ import 'package:day_picker/day_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.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'; @@ -46,7 +47,7 @@ class CreateTemporaryPassword extends StatelessWidget { ), leading: IconButton(onPressed: () { Navigator.of(context).pop('UpdatePage'); - }, icon: Icon(Icons.arrow_back)), + }, icon: const Icon(Icons.arrow_back)), actions: [ TextButton( onPressed: () { @@ -123,6 +124,11 @@ class CreateTemporaryPassword extends StatelessWidget { ], ), ), + BlocProvider.of(context).passwordController.text.isNotEmpty? + TextButton(onPressed: () async { + await Clipboard.setData(ClipboardData(text: BlocProvider.of(context).passwordController.text)); + }, + child: const Text('Copy')):SizedBox(), const SizedBox( height: 20, ), @@ -192,6 +198,7 @@ class CreateTemporaryPassword extends StatelessWidget { const SizedBox( height: 20, ), + if(type=='Online Password') DefaultContainer( padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5), diff --git a/lib/features/devices/view/widgets/smart_door/temporary_password_page.dart b/lib/features/devices/view/widgets/smart_door/temporary_password_page.dart index 7efadb7..ae9ad6b 100644 --- a/lib/features/devices/view/widgets/smart_door/temporary_password_page.dart +++ b/lib/features/devices/view/widgets/smart_door/temporary_password_page.dart @@ -49,7 +49,6 @@ class TemporaryPasswordPage extends StatelessWidget { Navigator.of(context).push(MaterialPageRoute(builder: (context) => ViewTemporaryPassword(deviceId:deviceId,type:'Online Password'),)); }, trailing: const Icon( - Icons.arrow_forward_ios, color: ColorsManager.greyColor, size: 15, diff --git a/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart b/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart index 1675e56..f778931 100644 --- a/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart +++ b/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart @@ -24,7 +24,6 @@ class ViewTemporaryPassword extends StatelessWidget { SmartDoorBloc(deviceId: deviceId!)..add(InitialPasswordsPage(type:type )), child: BlocConsumer( listener: (context, state) { - print('=======4${state}'); if (state is FailedState) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -35,7 +34,6 @@ class ViewTemporaryPassword extends StatelessWidget { } }, builder: (context, state) { final smartDoorBloc = BlocProvider.of(context); - return DefaultScaffold( title: 'Passwords', actions: [ @@ -45,9 +43,9 @@ class ViewTemporaryPassword extends StatelessWidget { builder: (context) => CreateTemporaryPassword(deviceId: deviceId, type: type), )).then((result) { if (result != null) { - smartDoorBloc.add(InitialPasswordsPage(type:type )); } + smartDoorBloc.add(InitialPasswordsPage(type:type )); + } }); - }, icon: const Icon(Icons.add)) ], From b00218ba5da6fd569413ba583b1295d7e9706918 Mon Sep 17 00:00:00 2001 From: mohammad Date: Sun, 30 Jun 2024 14:59:18 +0300 Subject: [PATCH 8/8] CreateTemporaryPassword --- .../bloc/smart_door_bloc/smart_door_bloc.dart | 72 +++++++++---------- .../smart_door_bloc/smart_door_event.dart | 43 +++++++++++ .../smart_door/create_temporary_password.dart | 38 +++++----- .../smart_door/view_temporary_password.dart | 4 +- lib/services/api/devices_api.dart | 4 +- 5 files changed, 102 insertions(+), 59 deletions(-) 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 2cae2e9..0259855 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 @@ -3,7 +3,6 @@ import 'package:day_picker/model/day_in_week.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:intl/intl.dart'; -import 'package:pin_code_fields/pin_code_fields.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'; import 'package:syncrow_app/features/devices/model/device_control_model.dart'; @@ -23,6 +22,13 @@ class SmartDoorBloc extends Bloc { on(_fetchSmartDoorStatus); on(getTemporaryPasswords); on(_updateLock); + on(savePassword); + on(toggleRepeat); + on(setStartEndTime); + on(changeTime); + on(generate7DigitNumber); + on(selectTime); + on(deletePassword); } void _fetchSmartDoorStatus( InitialEvent event, Emitter emit) async { @@ -78,26 +84,27 @@ class SmartDoorBloc extends Bloc { List? temporaryPasswords = []; List? oneTimePasswords = []; - changeTime(val, isStartEndTime) { + changeTime(ChangeTimeEvent event, Emitter emit) { emit(LoadingInitialState()); - if (isStartEndTime == true) { - startTime = val; + if (event.isStartEndTime == true) { + startTime = event.val; } else { - endTime = val; + endTime = event.val; } emit(changeTimeState()); } - bool toggleRepeat() { + bool toggleRepeat(ToggleRepeatEvent event, Emitter emit) { emit(LoadingInitialState()); repeat = !repeat; emit(IsRepeatState()); return repeat; } - bool setStartEndTime(bool val) { + + bool setStartEndTime(SetStartEndTimeEvent event, Emitter emit) { emit(LoadingInitialState()); - isStartEndTime = val; + isStartEndTime = event.val; emit(IsStartEndState()); return isStartEndTime; } @@ -119,7 +126,7 @@ class SmartDoorBloc extends Bloc { emit(UpdateState(smartDoorModel: deviceStatus)); } - void generate7DigitNumber() async { + void generate7DigitNumber(GeneratePasswordEvent event, Emitter emit) async { emit(LoadingInitialState()); passwordController.clear(); Random random = Random(); @@ -129,17 +136,16 @@ class SmartDoorBloc extends Bloc { emit(GeneratePasswordState()); } - Future selectTime(BuildContext context, - {required bool isEffective}) async { + Future selectTime(SelectTimeEvent event, Emitter emit) async { final DateTime? picked = await showDatePicker( - context: context, + context: event.context, initialDate: DateTime.now(), firstDate: DateTime(2015, 8), lastDate: DateTime(2101), ); if (picked != null) { final TimeOfDay? timePicked = await showTimePicker( - context: context, + context: event.context, initialTime: TimeOfDay.now(), ); if (timePicked != null) { @@ -150,12 +156,11 @@ class SmartDoorBloc extends Bloc { timePicked.hour, timePicked.minute, ); - if (isEffective) { + if (event.isEffective) { if (expirationTimeTimeStamp != null && selectedDateTime.millisecondsSinceEpoch > expirationTimeTimeStamp!) { - _showSnackBar(context, - 'Effective Time cannot be later than Expiration Time.'); + CustomSnackBar.displaySnackBar('Effective Time cannot be later than Expiration Time.'); } else { effectiveTime = selectedDateTime.toString(); effectiveTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; @@ -164,8 +169,7 @@ class SmartDoorBloc extends Bloc { if (effectiveTimeTimeStamp != null && selectedDateTime.millisecondsSinceEpoch < effectiveTimeTimeStamp!) { - _showSnackBar(context, - 'Expiration Time cannot be earlier than Effective Time.'); + CustomSnackBar.displaySnackBar('Expiration Time cannot be earlier than Effective Time.'); } else { expirationTime = selectedDateTime.toString(); expirationTimeTimeStamp = selectedDateTime.millisecondsSinceEpoch; @@ -176,11 +180,11 @@ class SmartDoorBloc extends Bloc { } } - Future savePassword(BuildContext context) async { - if (_validateInputs(context)) return; + Future savePassword(SavePasswordEvent event, Emitter emit) async { + if (_validateInputs()) return; try { emit(LoadingSaveState()); - var response = await DevicesAPI.createPassword( + var res = await DevicesAPI.createPassword( pageType: pageType, deviceId: deviceId, effectiveTime: effectiveTimeTimeStamp.toString(), @@ -195,21 +199,20 @@ class SmartDoorBloc extends Bloc { workingDay: selectedDay!, ), ], - ).then((value)async { - add(InitialPasswordsPage(type: pageType)); - },); + ); + Navigator.of(event.context).pop(true); CustomSnackBar.displaySnackBar('Save Successfully'); emit(SaveState()); - } catch (e) { - emit(FailedState(errorMessage: 'Failed to save password: $e')); + + } catch (_) { } } - void deletePassword(BuildContext context, passwordId) async { + Future deletePassword(DeletePasswordEvent event, Emitter emit) async { try { emit(LoadingInitialState()); var response = await DevicesAPI.deletePassword( - deviceId: deviceId, passwordId: passwordId) + deviceId: deviceId, passwordId: event.passwordId) .then((value) async { add(InitialPasswordsPage(type: pageType)); }); @@ -218,7 +221,7 @@ class SmartDoorBloc extends Bloc { } } - bool _validateInputs(BuildContext context) { + bool _validateInputs() { if (passwordController.text.length<7) { CustomSnackBar.displaySnackBar('Password less than 7'); return true; @@ -241,20 +244,13 @@ class SmartDoorBloc extends Bloc { } if (repeat == true && (endTime == null || startTime == null || selectedDay == null)) { - _showSnackBar(context, 'Start Time and End time and the days required '); + CustomSnackBar.displaySnackBar('Start Time and End time and the days required '); return true; } return false; } - void _showSnackBar(BuildContext context, String message) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(message), - backgroundColor: Colors.red, - ), - ); - } + List days = [ DayInWeek("S", dayKey: 'Sun'), diff --git a/lib/features/devices/bloc/smart_door_bloc/smart_door_event.dart b/lib/features/devices/bloc/smart_door_bloc/smart_door_event.dart index 0a481ab..df138ae 100644 --- a/lib/features/devices/bloc/smart_door_bloc/smart_door_event.dart +++ b/lib/features/devices/bloc/smart_door_bloc/smart_door_event.dart @@ -1,4 +1,5 @@ import 'package:equatable/equatable.dart'; +import 'package:flutter/cupertino.dart'; abstract class SmartDoorEvent extends Equatable { const SmartDoorEvent(); @@ -21,4 +22,46 @@ class UpdateLockEvent extends SmartDoorEvent { List get props => [value]; } +class SavePasswordEvent extends SmartDoorEvent { + final BuildContext context; + const SavePasswordEvent({required this.context}); + @override + List get props => [context]; +} + +class GeneratePasswordEvent extends SmartDoorEvent {} +class SelectTimeEvent extends SmartDoorEvent { + final BuildContext context; + final bool isEffective; + const SelectTimeEvent({required this.context,required this.isEffective}); + @override + List get props => [context,isEffective]; +} + +class ToggleRepeatEvent extends SmartDoorEvent {} +class SetStartEndTimeEvent extends SmartDoorEvent { + final bool val; + + const SetStartEndTimeEvent({required this.val}); + @override + List get props => [val]; +} + +class DeletePasswordEvent extends SmartDoorEvent { + final String passwordId; + + const DeletePasswordEvent({required this.passwordId}); + @override + List get props => [passwordId]; +} + +class ChangeTimeEvent extends SmartDoorEvent { + final dynamic val; + final bool isStartEndTime; + + const ChangeTimeEvent({required this.val,required this.isStartEndTime}); + @override + List get props => [val,isStartEndTime]; +} + 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 6fd500c..fd2af90 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 @@ -51,8 +51,7 @@ class CreateTemporaryPassword extends StatelessWidget { actions: [ TextButton( onPressed: () { - BlocProvider.of(context).savePassword(context); - SmartDoorBloc(deviceId: deviceId!).add(InitialPasswordsPage(type:type )); + BlocProvider.of(context).add(SavePasswordEvent(context: context)); }, child: const Text('Save')) ], @@ -84,9 +83,9 @@ class CreateTemporaryPassword extends StatelessWidget { child: Row( children: [ Expanded( - child: PinCodeTextField( - autoDisposeControllers: false, - keyboardType: TextInputType.phone, + child: PinCodeTextField( + autoDisposeControllers: false, + keyboardType: TextInputType.phone, length: 7, enabled: true, obscureText: false, @@ -114,7 +113,7 @@ class CreateTemporaryPassword extends StatelessWidget { ), InkWell( onTap: () { - BlocProvider.of(context).generate7DigitNumber(); + BlocProvider.of(context).add(GeneratePasswordEvent()); }, child: const BodyMedium( text: 'Generate Randomly', @@ -162,8 +161,8 @@ class CreateTemporaryPassword extends StatelessWidget { width: MediaQuery.of(context).size.width / 2, child: InkWell( onTap: () { - BlocProvider.of(context) - .selectTime(context, isEffective: true); + BlocProvider.of(context)..add(SelectTimeEvent(context: context, isEffective: true)); + // .selectTime(context, isEffective: true); }, child: Text( BlocProvider.of(context).effectiveTime @@ -183,9 +182,10 @@ class CreateTemporaryPassword extends StatelessWidget { width: MediaQuery.of(context).size.width / 2, child: InkWell( onTap: () { - BlocProvider.of(context) - .selectTime(context, - isEffective: false); + BlocProvider.of(context)..add(SelectTimeEvent(context: context, isEffective: false)); + // BlocProvider.of(context) + // .selectTime(context, + // isEffective: false); }, child: Text( BlocProvider.of(context) @@ -214,8 +214,7 @@ class CreateTemporaryPassword extends StatelessWidget { value: BlocProvider.of(context).repeat, onChanged: (value) { - BlocProvider.of(context) - .toggleRepeat(); + BlocProvider.of(context).add(ToggleRepeatEvent()); }, applyTheme: true, )), @@ -238,14 +237,15 @@ class CreateTemporaryPassword extends StatelessWidget { children: [ InkWell( onTap: () { - BlocProvider.of(context).setStartEndTime(true); + BlocProvider.of(context).add(const SetStartEndTimeEvent(val: true)); }, child: BodyMedium(text: 'Start',fontColor:BlocProvider.of(context).isStartEndTime==false? Colors.black:Colors.blue,fontSize: 18,), ), InkWell( onTap: () { - BlocProvider.of(context).setStartEndTime(false); + BlocProvider.of(context).add(const SetStartEndTimeEvent(val: false)); + }, child: BodyMedium(text: 'End',fontColor:BlocProvider.of(context).isStartEndTime? Colors.black:Colors.blue,fontSize: 18,), ) @@ -269,7 +269,9 @@ class CreateTemporaryPassword extends StatelessWidget { highlightedTextStyle: const TextStyle( fontSize: 30, color: Colors.blue), onTimeChange: (time) { - BlocProvider.of(context).changeTime(time, BlocProvider.of(context).isStartEndTime); + + // BlocProvider.of(context).changeTime(time, BlocProvider.of(context).isStartEndTime); + BlocProvider.of(context).add(ChangeTimeEvent(val: time, isStartEndTime: BlocProvider.of(context).isStartEndTime)); }, ): Container( child: TimePickerSpinner( @@ -282,7 +284,9 @@ class CreateTemporaryPassword extends StatelessWidget { highlightedTextStyle: const TextStyle( fontSize: 30, color: Colors.blue), onTimeChange: (time) { - BlocProvider.of(context).changeTime(time, BlocProvider.of(context).isStartEndTime); + BlocProvider.of(context).add(ChangeTimeEvent(val: time, isStartEndTime: BlocProvider.of(context).isStartEndTime)); + + // BlocProvider.of(context).changeTime(time, BlocProvider.of(context).isStartEndTime); }, ), ), diff --git a/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart b/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart index f778931..75350e4 100644 --- a/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart +++ b/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart @@ -42,7 +42,7 @@ class ViewTemporaryPassword extends StatelessWidget { Navigator.of(context).push(MaterialPageRoute( builder: (context) => CreateTemporaryPassword(deviceId: deviceId, type: type), )).then((result) { - if (result != null) { + if (result != null && result) { smartDoorBloc.add(InitialPasswordsPage(type:type )); } }); @@ -83,7 +83,7 @@ class ViewTemporaryPassword extends StatelessWidget { }, ); if(result=='delete'){ - smartDoorBloc.deletePassword(context, smartDoorBloc.temporaryPasswords![index].id.toString()); + smartDoorBloc.add(DeletePasswordEvent(passwordId: smartDoorBloc.temporaryPasswords![index].id.toString())); } }, trailing: const Icon( diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index a9a7b82..0cd1862 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -158,7 +158,7 @@ class DevicesAPI { required String pageType, List? scheduleList, }) async { - try { + String endpointPath; if (pageType == 'Online Password') { endpointPath = ApiEndpoints.addTemporaryPassword @@ -187,7 +187,7 @@ class DevicesAPI { expectedResponseModel: (json) => json, ); return response; - } catch (e) {} + }