From 9c838ed079b1bb24c8b28d274227df13a1ac8e07 Mon Sep 17 00:00:00 2001 From: mohammad Date: Sun, 4 Aug 2024 16:45:07 +0300 Subject: [PATCH] door lock --- .../bloc/smart_door_bloc/smart_door_bloc.dart | 78 ++++++++---- .../smart_door_bloc/smart_door_event.dart | 6 +- .../devices/model/offline_password_model.dart | 63 ++++++++++ .../offline_one_time_password_page.dart | 20 ++-- .../view/widgets/smart_door/door_dialog.dart | 51 ++++++-- .../offline_timeLimit_password_page.dart | 17 +-- .../smart_door/onetime_password_page.dart | 111 ++++++++++++++++++ .../smart_door/temporary_password_page.dart | 6 +- .../smart_door/timelimited_password_page.dart | 109 +++++++++++++++++ .../smart_door/view_temporary_password.dart | 17 +-- .../shared_widgets/default_button.dart | 15 +-- .../shared_widgets/door_lock_button.dart | 99 ++++++++++++++++ lib/services/api/api_links_endpoints.dart | 2 +- lib/services/api/devices_api.dart | 58 ++++++--- 14 files changed, 557 insertions(+), 95 deletions(-) create mode 100644 lib/features/devices/model/offline_password_model.dart create mode 100644 lib/features/devices/view/widgets/smart_door/onetime_password_page.dart create mode 100644 lib/features/devices/view/widgets/smart_door/timelimited_password_page.dart create mode 100644 lib/features/shared_widgets/door_lock_button.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 eedf10f..adf74e6 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 @@ -6,6 +6,7 @@ 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/offline_password_model.dart'; import 'package:syncrow_app/features/devices/model/offline_temporary_password.dart'; import 'package:syncrow_app/features/devices/model/smart_door_model.dart'; import 'package:syncrow_app/features/devices/model/status_model.dart'; @@ -14,15 +15,17 @@ 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'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + class SmartDoorBloc extends Bloc { final String deviceId; late SmartDoorModel deviceStatus; - static String pageType = ''; bool isSavingPassword = false; SmartDoorBloc({required this.deviceId}) : super(InitialState()) { on(_fetchSmartDoorStatus); on(getTemporaryPasswords); + on(getOneTimePasswords); + on(getTimeLimitPasswords); on(_updateLock); on(savePassword); on(toggleRepeat); @@ -49,7 +52,8 @@ class SmartDoorBloc extends Bloc { DateTime? startTime; DateTime? endTime; List? temporaryPasswords = []; - List? oneTimePasswords = []; + List? oneTimePasswords = []; + List? timeLimitPasswords = []; Future generate7DigitNumber(GeneratePasswordEvent event, Emitter emit) async { emit(LoadingInitialState()); @@ -72,8 +76,7 @@ class SmartDoorBloc extends Bloc { ApiResponse pass= ApiResponse.fromJson(res); passwordController.text =pass.data.offlineTempPassword; passwordId=pass.data.offlineTempPasswordId; - emit(const GeneratePasswordOneTimestate(generated: true)); - + emit(const GeneratePasswordOneTimestate(generated: true)); } catch (_) { emit(FailedState(errorMessage: _.toString())); }finally { @@ -100,9 +103,12 @@ class SmartDoorBloc extends Bloc { void _renamePassword(RenamePasswordEvent event, Emitter emit) async { try { emit(LoadingInitialState()); + print('==4444${passwordNameController.text}'); + print('==1==${deviceId}'); + print('==4444${passwordId}'); var response = await DevicesAPI.renamePass( name:passwordNameController.text , - deviceId:deviceId , + doorLockUuid:deviceId , passwordId:passwordId ); emit(UpdateState(smartDoorModel: deviceStatus)); @@ -115,14 +121,37 @@ class SmartDoorBloc extends Bloc { void getTemporaryPasswords(InitialPasswordsPage event, Emitter emit) async { try { emit(LoadingInitialState()); - print('deviceId=$deviceId'); - pageType = event.type!; - var response = await DevicesAPI.getTemporaryPasswords(deviceId, pageType); + var response = await DevicesAPI.getTemporaryPasswords(deviceId, ); 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(); + temporaryPasswords = (response['data'] as List).map((item) => TemporaryPassword.fromJson(item)).toList(); + } + emit(TemporaryPasswordsLoadedState(temporaryPassword: temporaryPasswords!)); + } catch (e) { + emit(FailedState(errorMessage: e.toString())); + } + } + + void getOneTimePasswords(InitialOneTimePassword event, Emitter emit) async { + try { + emit(LoadingInitialState()); + var response = await DevicesAPI.getOneTimePasswords(deviceId); + if (response is List) { + oneTimePasswords = response.map((item) => OfflinePasswordModel.fromJson(item)).toList(); + } + emit(TemporaryPasswordsLoadedState(temporaryPassword: temporaryPasswords!)); + } catch (e) { + emit(FailedState(errorMessage: e.toString())); + } + } + + void getTimeLimitPasswords(InitialTimeLimitPassword event, Emitter emit) async { + try { + emit(LoadingInitialState()); + var response = await DevicesAPI.getTimeLimitPasswords(deviceId); + if (response is List) { + timeLimitPasswords = response.map((item) => OfflinePasswordModel.fromJson(item)).toList(); } emit(TemporaryPasswordsLoadedState(temporaryPassword: temporaryPasswords!)); } catch (e) { @@ -166,8 +195,6 @@ class SmartDoorBloc extends Bloc { emit(UpdateState(smartDoorModel: deviceStatus)); } - - Future selectTime(SelectTimeEvent event, Emitter emit) async { emit(ChangeTimeState()); final DateTime? picked = await showDatePicker( @@ -241,7 +268,6 @@ class SmartDoorBloc extends Bloc { isSavingPassword = true; emit(LoadingSaveState()); var res = await DevicesAPI.createPassword( - pageType: pageType, deviceId: deviceId, effectiveTime: effectiveTimeTimeStamp.toString(), invalidTime: expirationTimeTimeStamp.toString(), @@ -267,7 +293,7 @@ class SmartDoorBloc extends Bloc { } Future generateAndSavePasswordTimeLimited (GenerateAndSavePasswordTimeLimitEvent event, Emitter emit) async { - if (_validateInputs() || isSavingPassword) return; + if (timeLimitValidate() || isSavingPassword) return; try { isSavingPassword = true; emit(LoadingInitialState()); @@ -280,22 +306,20 @@ class SmartDoorBloc extends Bloc { CustomSnackBar.displaySnackBar('Save Successfully'); emit(const GeneratePasswordOneTimestate(generated: true)); } catch (_) { - print(_); + emit(FailedState(errorMessage: e.toString())); } finally { isSavingPassword = false; } } - - Future deletePassword(DeletePasswordEvent event, Emitter emit) async { try { emit(LoadingInitialState()); var response = await DevicesAPI.deletePassword(deviceId: deviceId, passwordId: event.passwordId) .then((value) async { - add(InitialPasswordsPage(type: pageType)); + add(InitialPasswordsPage()); }); } catch (e) { emit(FailedState(errorMessage: e.toString())); @@ -303,15 +327,15 @@ class SmartDoorBloc extends Bloc { } bool _validateInputs() { - if (passwordController.text.length < 7 && pageType=='Online Password') { + if (passwordController.text.length < 7 ) { CustomSnackBar.displaySnackBar('Password less than 7'); return true; } - if (passwordController.text.isEmpty && pageType=='Online Password') { + if (passwordController.text.isEmpty) { CustomSnackBar.displaySnackBar('Password required'); return true; } - if (passwordNameController.text.isEmpty && pageType=='Online Password') { + if (passwordNameController.text.isEmpty ) { CustomSnackBar.displaySnackBar('Password name required'); return true; } @@ -329,7 +353,18 @@ class SmartDoorBloc extends Bloc { } return false; } + bool timeLimitValidate() { + if (effectiveTime == 'Select Time' || effectiveTimeTimeStamp == null) { + CustomSnackBar.displaySnackBar('Select effective time'); + return true; + } + if (expirationTime == 'Select Time' || expirationTimeTimeStamp == null) { + CustomSnackBar.displaySnackBar('Select expiration time'); + return true; + } + return false; + } List> days = [ {"day": "Sun", "key": "Sun"}, @@ -352,6 +387,7 @@ class SmartDoorBloc extends Bloc { } emit(ChangeTimeState()); } + 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 ca4f7b6..b73a381 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 @@ -9,11 +9,9 @@ abstract class SmartDoorEvent extends Equatable { } class InitialEvent extends SmartDoorEvent {} -class InitialPasswordsPage extends SmartDoorEvent { - final String? type; - const InitialPasswordsPage({ this.type}); -} +class InitialPasswordsPage extends SmartDoorEvent {} class InitialOneTimePassword extends SmartDoorEvent {} +class InitialTimeLimitPassword extends SmartDoorEvent {} class UpdateLockEvent extends SmartDoorEvent { final bool value; diff --git a/lib/features/devices/model/offline_password_model.dart b/lib/features/devices/model/offline_password_model.dart new file mode 100644 index 0000000..17d8faa --- /dev/null +++ b/lib/features/devices/model/offline_password_model.dart @@ -0,0 +1,63 @@ + + +class OfflinePasswordModel { + final dynamic gmtCreate; + final dynamic gmtExpired; + final dynamic gmtStart; + final bool hasClearPwd; + final String optUid; + final String pwd; + final dynamic pwdId; + final String pwdName; + final String pwdTypeCode; + final String revokedPwdName; + final dynamic status; + + OfflinePasswordModel({ + required this.gmtCreate, + required this.gmtExpired, + required this.gmtStart, + required this.hasClearPwd, + required this.optUid, + required this.pwd, + required this.pwdId, + required this.pwdName, + required this.pwdTypeCode, + required this.revokedPwdName, + required this.status, + }); + + // Factory method to create a Password from a JSON map + factory OfflinePasswordModel.fromJson(Map json) { + return OfflinePasswordModel( + gmtCreate: json['gmtCreate'], + gmtExpired: json['gmtExpired'], + gmtStart: json['gmtStart'], + hasClearPwd: json['hasClearPwd'], + optUid: json['optUid'], + pwd: json['pwd'], + pwdId: json['pwdId'], + pwdName: json['pwdName'], + pwdTypeCode: json['pwdTypeCode'], + revokedPwdName: json['revokedPwdName'], + status: json['status'], + ); + } + + // Method to convert a Password object to a JSON map + Map toJson() { + return { + 'gmtCreate': gmtCreate, + 'gmtExpired': gmtExpired, + 'gmtStart': gmtStart, + 'hasClearPwd': hasClearPwd, + 'optUid': optUid, + 'pwd': pwd, + 'pwdId': pwdId, + 'pwdName': pwdName, + 'pwdTypeCode': pwdTypeCode, + 'revokedPwdName': revokedPwdName, + 'status': status, + }; + } +} \ No newline at end of file diff --git a/lib/features/devices/view/widgets/offline_one_time_password_page.dart b/lib/features/devices/view/widgets/offline_one_time_password_page.dart index 204504e..5c75842 100644 --- a/lib/features/devices/view/widgets/offline_one_time_password_page.dart +++ b/lib/features/devices/view/widgets/offline_one_time_password_page.dart @@ -1,6 +1,4 @@ - - import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -12,12 +10,12 @@ import 'package:syncrow_app/features/devices/view/widgets/smart_door/repeat_widg import 'package:syncrow_app/features/shared_widgets/default_button.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/door_lock_button.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'; - class OfflineOneTimePasswordPage extends StatelessWidget { final String? deviceId; final String? type; @@ -102,8 +100,7 @@ class OfflineOneTimePasswordPage extends StatelessWidget { color: Colors.black, ), ); - }) - :[ + }) :[ Expanded( child: Row( children: [ @@ -158,8 +155,7 @@ class OfflineOneTimePasswordPage extends StatelessWidget { SizedBox( width: MediaQuery.of(context).size.width / 2.6, child: TextFormField( - controller: BlocProvider.of(context) - .passwordNameController, + controller: BlocProvider.of(context).passwordNameController, decoration: const InputDecoration( hintText: 'Enter The Name', hintStyle: TextStyle( @@ -191,22 +187,26 @@ class OfflineOneTimePasswordPage extends StatelessWidget { Center( child: SizedBox( width: MediaQuery.of(context).size.width/1.5, - child: DefaultButton( + child: DoorLockButton( isDone: generated, isLoading: smartDoorBloc.isSavingPassword , borderRadius: 30, backgroundColor:ColorsManager.primaryColor , - onPressed: () { + onPressed: () async { if(generated==false){ smartDoorBloc.add(GenerateAndSavePasswordOneTimeEvent(context: context)); + await Clipboard.setData( + ClipboardData(text: smartDoorBloc.passwordController.text) + ); }else{ if(smartDoorBloc.passwordNameController.text.isNotEmpty){ smartDoorBloc.add(RenamePasswordEvent()); } - Navigator.pop(context); + Navigator.of(context).pop(true); } }, child: const BodyMedium( + text: 'Obtain Password', fontWeight: FontWeight.bold, fontColor: Colors.white, diff --git a/lib/features/devices/view/widgets/smart_door/door_dialog.dart b/lib/features/devices/view/widgets/smart_door/door_dialog.dart index 39d83b9..281b91e 100644 --- a/lib/features/devices/view/widgets/smart_door/door_dialog.dart +++ b/lib/features/devices/view/widgets/smart_door/door_dialog.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:syncrow_app/features/devices/model/offline_password_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/utils/context_extension.dart'; @@ -7,13 +8,15 @@ 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; + final String? title; + final TemporaryPassword? temporaryPassword; + final OfflinePasswordModel? offline; const DoorDialog({ super.key, - required this.title, - required this.value, + this.title, + this.offline, + this.temporaryPassword, }); @override @@ -28,16 +31,18 @@ class DoorDialogState extends State { @override Widget build(BuildContext context) { + final effectiveTime = widget.temporaryPassword?.effectiveTime ??int.parse( widget.offline?.gmtStart); + final invalidTime = widget.temporaryPassword?.invalidTime ?? int.parse(widget.offline?.gmtExpired); + final DateTime effectiveDateTime = - DateTime.fromMillisecondsSinceEpoch(widget.value.effectiveTime * 1000, isUtc: false); + DateTime.fromMillisecondsSinceEpoch(effectiveTime! * 1000, isUtc: false); String formattedDateEffectiveTime = DateFormat('yyyy-MM-dd').format(effectiveDateTime); - String formattedTimeEffectiveTime = DateFormat('HH:mm:ss').format(effectiveDateTime); + String formattedTimeEffectiveTime = DateFormat('hh:mm a').format(effectiveDateTime); final DateTime expiredDateTime = - DateTime.fromMillisecondsSinceEpoch(widget.value.invalidTime * 1000, isUtc: false); + DateTime.fromMillisecondsSinceEpoch(invalidTime! * 1000, isUtc: false); String formattedDateExpiredDateTime = DateFormat('yyyy-MM-dd').format(expiredDateTime); - String formattedTimeExpiredDateTime = DateFormat('HH:mm:ss').format(expiredDateTime); - + String formattedTimeExpiredDateTime = DateFormat('hh:mm a').format(expiredDateTime); return Dialog( child: Container( decoration: BoxDecoration( @@ -49,7 +54,7 @@ class DoorDialogState extends State { mainAxisSize: MainAxisSize.min, children: [ BodyMedium( - text: widget.title, + text: widget.title ?? '', style: context.bodyMedium.copyWith( color: ColorsManager.primaryColorWithOpacity, fontWeight: FontsManager.extraBold, @@ -111,7 +116,8 @@ class DoorDialogState extends State { width: double.infinity, color: ColorsManager.greyColor, ), - Row( + widget.temporaryPassword?.effectiveTime!=null? + Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ InkWell( @@ -130,7 +136,7 @@ class DoorDialogState extends State { width: 1, color: ColorsManager.greyColor, ), - InkWell( + InkWell( onTap: () { Navigator.pop(context, 'delete'); }, @@ -142,6 +148,27 @@ class DoorDialogState extends State { ), ), ], + ): + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + height: 50, + child: InkWell( + onTap: () { + Navigator.pop(context); + }, + child: Center( + child: BodyMedium( + text: 'Cancel', + style: context.bodyMedium.copyWith(color: ColorsManager.greyColor), + ), + ), + ), + + ), + + ], ) ], ), diff --git a/lib/features/devices/view/widgets/smart_door/offline_timeLimit_password_page.dart b/lib/features/devices/view/widgets/smart_door/offline_timeLimit_password_page.dart index 35b0eb4..bc21c27 100644 --- a/lib/features/devices/view/widgets/smart_door/offline_timeLimit_password_page.dart +++ b/lib/features/devices/view/widgets/smart_door/offline_timeLimit_password_page.dart @@ -9,15 +9,16 @@ import 'package:syncrow_app/features/devices/view/widgets/smart_door/repeat_widg import 'package:syncrow_app/features/shared_widgets/default_button.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/door_lock_button.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'; -class OfflineTimeLimitPasswordPage extends StatelessWidget { +class CreateOfflineTimeLimitPasswordPage extends StatelessWidget { final String? deviceId; final String? type; - const OfflineTimeLimitPasswordPage({super.key, this.deviceId, this.type}); + const CreateOfflineTimeLimitPasswordPage({super.key, this.deviceId, this.type}); @override Widget build(BuildContext context) { bool isRepeat = false; @@ -253,21 +254,23 @@ class OfflineTimeLimitPasswordPage extends StatelessWidget { Center( child: SizedBox( width: MediaQuery.of(context).size.width / 1.5, - child: DefaultButton( + child: DoorLockButton( isDone: generated, isLoading: smartDoorBloc.isSavingPassword, borderRadius: 30, backgroundColor: ColorsManager.primaryColor, - onPressed: () { + onPressed: () async { if (generated == false) { smartDoorBloc.add( - GenerateAndSavePasswordTimeLimitEvent( - context: context)); + GenerateAndSavePasswordTimeLimitEvent(context: context)); + await Clipboard.setData( + ClipboardData(text: smartDoorBloc.passwordController.text) + ); } else { if(smartDoorBloc.passwordNameController.text.isNotEmpty){ smartDoorBloc.add(RenamePasswordEvent()); } - Navigator.pop(context); + Navigator.of(context).pop(true); } }, child: const BodyMedium( diff --git a/lib/features/devices/view/widgets/smart_door/onetime_password_page.dart b/lib/features/devices/view/widgets/smart_door/onetime_password_page.dart new file mode 100644 index 0000000..1b1e3fb --- /dev/null +++ b/lib/features/devices/view/widgets/smart_door/onetime_password_page.dart @@ -0,0 +1,111 @@ +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/devices/view/widgets/offline_one_time_password_page.dart'; +import 'package:syncrow_app/features/devices/view/widgets/smart_door/door_dialog.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/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class OnetimePasswordPage extends StatelessWidget { + final String? deviceId; + const OnetimePasswordPage({super.key, this.deviceId,}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!)..add(InitialOneTimePassword( )), + child: BlocConsumer( + listener: (context, state) { + if (state is FailedState) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(state.errorMessage), + backgroundColor: Colors.red, + ), + ); + } + }, + builder: (context, state) { + final smartDoorBloc = BlocProvider.of(context); + return DefaultScaffold( + title: 'Passwords', + actions: [ + IconButton( + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute(builder: (context) => OfflineOneTimePasswordPage(deviceId: deviceId, ) + )).then((result) { + if(result!=null){ + smartDoorBloc.add(InitialOneTimePassword()); + smartDoorBloc.add(InitialOneTimePassword()); + } + }); + }, + icon: const Icon(Icons.add) + ) + ], + child: Builder( + builder: (context) { + return state is LoadingInitialState + ? const Center(child: CircularProgressIndicator()) + : Center( + child: smartDoorBloc.oneTimePasswords!.isNotEmpty + ? ListView.builder( + itemCount: smartDoorBloc.oneTimePasswords!.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.oneTimePasswords![index].pwdName}', + fontWeight: FontWeight.normal, + ), + onTap: () async { + final result = await showDialog( + context: context, + builder: (context) { + return DoorDialog( + title: 'Password Information', + offline: smartDoorBloc.oneTimePasswords![index], + ); + }, + ); + + }, + 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/features/devices/view/widgets/smart_door/temporary_password_page.dart b/lib/features/devices/view/widgets/smart_door/temporary_password_page.dart index 8a7f7e8..15c7dbf 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 @@ -1,6 +1,8 @@ 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/onetime_password_page.dart'; +import 'package:syncrow_app/features/devices/view/widgets/smart_door/timelimited_password_page.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'; @@ -90,7 +92,7 @@ class TemporaryPasswordPage extends StatelessWidget { fontWeight: FontWeight.normal, ), onTap: () { - Navigator.of(context).push(MaterialPageRoute(builder: (context) => ViewTemporaryPassword(deviceId:deviceId,type:'One-Time'),)); + Navigator.of(context).push(MaterialPageRoute(builder: (context) => OnetimePasswordPage(deviceId:deviceId,),)); }, trailing: const Icon( Icons.arrow_forward_ios, @@ -108,7 +110,7 @@ class TemporaryPasswordPage extends StatelessWidget { fontWeight: FontWeight.normal, ), onTap: () { - Navigator.of(context).push(MaterialPageRoute(builder: (context) => ViewTemporaryPassword(deviceId:deviceId,type:'Time-Limited'),)); + Navigator.of(context).push(MaterialPageRoute(builder: (context) => TimeLimitedPasswordPage(deviceId:deviceId,),)); }, trailing: const Icon( Icons.arrow_forward_ios, diff --git a/lib/features/devices/view/widgets/smart_door/timelimited_password_page.dart b/lib/features/devices/view/widgets/smart_door/timelimited_password_page.dart new file mode 100644 index 0000000..644c075 --- /dev/null +++ b/lib/features/devices/view/widgets/smart_door/timelimited_password_page.dart @@ -0,0 +1,109 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/flutter_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/devices/view/widgets/smart_door/offline_timeLimit_password_page.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/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import 'door_dialog.dart'; + +class TimeLimitedPasswordPage extends StatelessWidget { + final String? deviceId; + const TimeLimitedPasswordPage({super.key, this.deviceId}); + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!)..add(InitialTimeLimitPassword()), + child: BlocConsumer( + listener: (context, state) { + if (state is FailedState) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(state.errorMessage), + backgroundColor: Colors.red, + ), + ); + } + }, + builder: (context, state) { + final smartDoorBloc = BlocProvider.of(context); + return DefaultScaffold( + title: 'Passwords', + actions: [ + IconButton( + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute(builder: (context) => CreateOfflineTimeLimitPasswordPage(deviceId: deviceId,) + )).then((result) { + if (result != null) { + smartDoorBloc.add(InitialTimeLimitPassword()); + smartDoorBloc.add(InitialTimeLimitPassword()); + } + }); + }, + icon: const Icon(Icons.add) + ) + ], + child: Builder( + builder: (context) { + return state is LoadingInitialState + ? const Center(child: CircularProgressIndicator()) + : Center( + child: smartDoorBloc.timeLimitPasswords!.isNotEmpty + ? ListView.builder( + itemCount: smartDoorBloc.timeLimitPasswords!.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.timeLimitPasswords![index].pwdName}', + fontWeight: FontWeight.normal, + ), + onTap: () async { + final result = await showDialog( + context: context, + builder: (context) { + return DoorDialog( + title: 'Password Information', + offline: smartDoorBloc.timeLimitPasswords![index], + ); + }, + ); + }, + 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/features/devices/view/widgets/smart_door/view_temporary_password.dart b/lib/features/devices/view/widgets/smart_door/view_temporary_password.dart index 4c426ed..03bf22f 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 @@ -4,8 +4,6 @@ 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/devices/view/widgets/offline_one_time_password_page.dart'; -import 'package:syncrow_app/features/devices/view/widgets/smart_door/offline_timeLimit_password_page.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'; @@ -22,7 +20,7 @@ class ViewTemporaryPassword extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!)..add(InitialPasswordsPage(type:type )), + create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!)..add(InitialPasswordsPage( )), child: BlocConsumer( listener: (context, state) { if (state is FailedState) { @@ -43,15 +41,10 @@ class ViewTemporaryPassword extends StatelessWidget { onPressed: () { Navigator.of(context).push( MaterialPageRoute(builder: (context) => - type=='One-Time' ? - OfflineOneTimePasswordPage(deviceId: deviceId, type: type) - : type=='Time-Limited' ? - OfflineTimeLimitPasswordPage(deviceId: deviceId, type: type) - : - CreateTemporaryPassword(deviceId: deviceId, type: type), - )).then((result) { + CreateTemporaryPassword(deviceId: deviceId, type: type))).then((result) { if (result != null && result) { - smartDoorBloc.add(InitialPasswordsPage(type:type )); + smartDoorBloc.add(InitialPasswordsPage()); + smartDoorBloc.add(InitialPasswordsPage()); } }); }, @@ -85,7 +78,7 @@ class ViewTemporaryPassword extends StatelessWidget { builder: (context) { return DoorDialog( title: 'Password Information', - value: smartDoorBloc.temporaryPasswords![index], + temporaryPassword: smartDoorBloc.temporaryPasswords![index], ); }, ); diff --git a/lib/features/shared_widgets/default_button.dart b/lib/features/shared_widgets/default_button.dart index 09ad431..3a1966e 100644 --- a/lib/features/shared_widgets/default_button.dart +++ b/lib/features/shared_widgets/default_button.dart @@ -29,7 +29,6 @@ class DefaultButton extends StatelessWidget { final double? padding; final bool isDone; final bool isLoading; - final TextStyle? customTextStyle; final ButtonStyle? customButtonStyle; @@ -91,12 +90,14 @@ class DefaultButton extends StatelessWidget { color: Colors.white, ), ) - : isDone - ? const Icon( - Icons.check_circle_outline, - color: Colors.white, - ) - : child, + : + + + isDone + ? const Icon( + Icons.check_circle_outline, + color: Colors.white, + ) :child , ), ), ); diff --git a/lib/features/shared_widgets/door_lock_button.dart b/lib/features/shared_widgets/door_lock_button.dart new file mode 100644 index 0000000..a178ea1 --- /dev/null +++ b/lib/features/shared_widgets/door_lock_button.dart @@ -0,0 +1,99 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class DoorLockButton extends StatelessWidget { + const DoorLockButton({ + super.key, + this.enabled = true, + this.onPressed, + required this.child, + this.isSecondary = false, + this.isLoading = false, + this.isDone = false, + this.customTextStyle, + this.customButtonStyle, + this.backgroundColor, + this.foregroundColor, + this.borderRadius, + this.height, + this.padding, + }); + + final void Function()? onPressed; + final Widget child; + final double? height; + final bool isSecondary; + final double? borderRadius; + final bool enabled; + final double? padding; + final bool isDone; + final bool isLoading; + final TextStyle? customTextStyle; + + final ButtonStyle? customButtonStyle; + + final Color? backgroundColor; + + final Color? foregroundColor; + + @override + Widget build(BuildContext context) { + return ElevatedButton( + onPressed: enabled ? onPressed : null, + style: isSecondary + ? null + : customButtonStyle ?? + ButtonStyle( + textStyle: MaterialStateProperty.all( + customTextStyle ?? + context.bodyMedium.copyWith( + fontSize: 16, + color: foregroundColor, + ), + ), + foregroundColor: MaterialStateProperty.all( + isSecondary + ? Colors.black + : enabled + ? foregroundColor ?? Colors.white + : Colors.black, + ), + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) { + return enabled + ? backgroundColor ?? ColorsManager.primaryColor + : Colors.grey; + }), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius ?? 20), + ), + ), + fixedSize: MaterialStateProperty.all( + const Size.fromHeight(50), + ), + padding: MaterialStateProperty.all( + EdgeInsets.all(padding ?? 10), + ), + minimumSize: MaterialStateProperty.all( + const Size.fromHeight(50), + ), + ), + child: SizedBox( + height: height ?? 50, + child: Center( + child: isLoading + ? const SizedBox.square( + dimension: 24, + child: CircularProgressIndicator( + color: Colors.white, + ), + ) + : isDone + ? const Text('Done') :child , + ), + ), + ); + } +} diff --git a/lib/services/api/api_links_endpoints.dart b/lib/services/api/api_links_endpoints.dart index 5babff5..4bdd3e9 100644 --- a/lib/services/api/api_links_endpoints.dart +++ b/lib/services/api/api_links_endpoints.dart @@ -153,5 +153,5 @@ abstract class ApiEndpoints { //multiple-time offline static const String deleteTemporaryPassword = - '$baseUrl/door-lock/temporary-password/{doorLockUuid}/{passwordId}'; + '$baseUrl/door-lock/temporary-password/online/{doorLockUuid}/{passwordId}'; } diff --git a/lib/services/api/devices_api.dart b/lib/services/api/devices_api.dart index 4c65f02..2e98690 100644 --- a/lib/services/api/devices_api.dart +++ b/lib/services/api/devices_api.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:developer'; import 'package:syncrow_app/features/devices/model/device_category_model.dart'; import 'package:syncrow_app/features/devices/model/device_control_model.dart'; import 'package:syncrow_app/features/devices/model/device_model.dart'; @@ -72,10 +73,12 @@ class DevicesAPI { static Future> renamePass({ required String name, - required String deviceId, + required String doorLockUuid, required String passwordId}) async { final response = await _httpService.put( - path: ApiEndpoints.renamePassword.replaceAll('{deviceUuid}', deviceId).replaceAll('{passwordId}', passwordId), + path: ApiEndpoints.renamePassword + .replaceAll('{doorLockUuid}', doorLockUuid) + .replaceAll('{passwordId}', passwordId), body: { "name": name }, @@ -155,10 +158,9 @@ class DevicesAPI { return response; } - static Future getTemporaryPasswords(String deviceId, pageType) async { + static Future getTemporaryPasswords(String deviceId, ) async { final response = await _httpService.get( - path: - ApiEndpoints.getTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + path: ApiEndpoints.getTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), showServerMessage: false, expectedResponseModel: (json) { return json; @@ -167,35 +169,50 @@ class DevicesAPI { return response; } -//create password + static Future getOneTimePasswords(String deviceId) async { + final response = await _httpService.get( + path: ApiEndpoints.getOneTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + showServerMessage: false, + expectedResponseModel: (json) { + log('==============$json'); + return json; + }, + ); + return response; + } + + static Future getTimeLimitPasswords(String deviceId) async { + final response = await _httpService.get( + path: ApiEndpoints.getMultipleTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), + showServerMessage: false, + expectedResponseModel: (json) { + print('00000000$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 { - 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); - } + List? scheduleList,}) async { + Map body = { "name": name, "password": password, "effectiveTime": effectiveTime, "invalidTime": invalidTime, }; - if (pageType == 'Online Password' && scheduleList != null) { + if (scheduleList != null) { body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList(); } final response = await _httpService.post( - path: endpointPath, + path: ApiEndpoints.addTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), body: body, showServerMessage: false, expectedResponseModel: (json) => json, @@ -209,6 +226,7 @@ class DevicesAPI { path: ApiEndpoints.addOneTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId), showServerMessage: false, expectedResponseModel: (json) { + print('generateOneTimePassword=${json}'); return json; }, ); @@ -250,4 +268,6 @@ class DevicesAPI { ); return response; } + } +