door lock password update

This commit is contained in:
mohammad
2024-08-06 14:58:06 +03:00
parent 905399f037
commit 0ef2f3b866
18 changed files with 1831 additions and 573 deletions

View File

@ -1,11 +1,13 @@
import 'dart:math'; import 'dart:math';
import 'package:day_picker/model/day_in_week.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.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_event.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.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/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/smart_door_model.dart';
import 'package:syncrow_app/features/devices/model/status_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/create_temporary_password_model.dart';
@ -17,12 +19,13 @@ import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> { class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
final String deviceId; final String deviceId;
late SmartDoorModel deviceStatus; late SmartDoorModel deviceStatus;
static String pageType = '';
bool isSavingPassword = false; bool isSavingPassword = false;
SmartDoorBloc({required this.deviceId}) : super(InitialState()) { SmartDoorBloc({required this.deviceId}) : super(InitialState()) {
on<InitialEvent>(_fetchSmartDoorStatus); on<InitialEvent>(_fetchSmartDoorStatus);
on<InitialPasswordsPage>(getTemporaryPasswords); on<InitialPasswordsPage>(getTemporaryPasswords);
on<InitialOneTimePassword>(getOneTimePasswords);
on<InitialTimeLimitPassword>(getTimeLimitPasswords);
on<UpdateLockEvent>(_updateLock); on<UpdateLockEvent>(_updateLock);
on<SavePasswordEvent>(savePassword); on<SavePasswordEvent>(savePassword);
on<ToggleRepeatEvent>(toggleRepeat); on<ToggleRepeatEvent>(toggleRepeat);
@ -31,7 +34,56 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
on<GeneratePasswordEvent>(generate7DigitNumber); on<GeneratePasswordEvent>(generate7DigitNumber);
on<SelectTimeEvent>(selectTime); on<SelectTimeEvent>(selectTime);
on<DeletePasswordEvent>(deletePassword); on<DeletePasswordEvent>(deletePassword);
on<GenerateAndSavePasswordTimeLimitEvent>(generateAndSavePasswordTimeLimited);
on<GenerateAndSavePasswordOneTimeEvent>(generateAndSavePasswordOneTime);
on<ToggleDaySelectionEvent>(toggleDaySelection);
on<RenamePasswordEvent>(_renamePassword);
} }
TextEditingController passwordController = TextEditingController();
TextEditingController passwordNameController = TextEditingController();
String effectiveTime = 'Select Time';
String passwordId = '';
int? effectiveTimeTimeStamp;
String expirationTime = 'Select Time';
int? expirationTimeTimeStamp;
bool repeat = false;
bool isStartEndTime = true;
DateTime? startTime;
DateTime? endTime;
List<TemporaryPassword>? temporaryPasswords = [];
List<OfflinePasswordModel>? oneTimePasswords = [];
List<OfflinePasswordModel>? timeLimitPasswords = [];
Future generate7DigitNumber(GeneratePasswordEvent event, Emitter<SmartDoorState> emit) async {
emit(LoadingInitialState());
passwordController.clear();
Random random = Random();
int min = 1000000;
int max = 9999999;
passwordController.text = (min + random.nextInt(max - min + 1)).toString();
emit(GeneratePasswordState());
return passwordController.text;
}
Future generateAndSavePasswordOneTime (GenerateAndSavePasswordOneTimeEvent event, Emitter<SmartDoorState> emit) async {
try {
if (isSavingPassword) return;
isSavingPassword = true;
emit(LoadingInitialState());
var res = await DevicesAPI.generateOneTimePassword(deviceId: deviceId);
ApiResponse pass= ApiResponse.fromJson(res);
passwordController.text =pass.data.offlineTempPassword;
passwordId=pass.data.offlineTempPasswordId;
emit(const GeneratePasswordOneTimestate(generated: true));
} catch (_) {
emit(FailedState(errorMessage: _.toString()));
}finally {
isSavingPassword = false;
}
}
void _fetchSmartDoorStatus(InitialEvent event, Emitter<SmartDoorState> emit) async { void _fetchSmartDoorStatus(InitialEvent event, Emitter<SmartDoorState> emit) async {
try { try {
emit(LoadingInitialState()); emit(LoadingInitialState());
@ -48,18 +100,29 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
} }
} }
void _renamePassword(RenamePasswordEvent event, Emitter<SmartDoorState> emit) async {
try {
emit(LoadingInitialState());
var response = await DevicesAPI.renamePass(
name:passwordNameController.text ,
doorLockUuid:deviceId ,
passwordId:passwordId
);
emit(UpdateState(smartDoorModel: deviceStatus));
} catch (e) {
emit(FailedState(errorMessage: e.toString()));
return;
}
}
void getTemporaryPasswords(InitialPasswordsPage event, Emitter<SmartDoorState> emit) async { void getTemporaryPasswords(InitialPasswordsPage event, Emitter<SmartDoorState> emit) async {
try { try {
emit(LoadingInitialState()); emit(LoadingInitialState());
pageType = event.type!; var response = await DevicesAPI.getTemporaryPasswords(deviceId, );
var response = await DevicesAPI.getTemporaryPasswords(deviceId, pageType);
if (response is List) { 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')) { } else if (response is Map && response.containsKey('data')) {
temporaryPasswords = temporaryPasswords = (response['data'] as List).map((item) => TemporaryPassword.fromJson(item)).toList();
(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) { } catch (e) {
@ -67,41 +130,51 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
} }
} }
TextEditingController passwordController = TextEditingController(); void getOneTimePasswords(InitialOneTimePassword event, Emitter<SmartDoorState> emit) async {
TextEditingController passwordNameController = TextEditingController(); try {
String effectiveTime = 'Select Time'; emit(LoadingInitialState());
int? effectiveTimeTimeStamp; var response = await DevicesAPI.getOneTimePasswords(deviceId);
String expirationTime = 'Select Time'; if (response is List) {
int? expirationTimeTimeStamp; oneTimePasswords = response.map((item) => OfflinePasswordModel.fromJson(item)).toList();
bool repeat = false; }
bool isStartEndTime = true; emit(TemporaryPasswordsLoadedState(temporaryPassword: temporaryPasswords!));
List<String>? selectedDay; } catch (e) {
DateTime? startTime; emit(FailedState(errorMessage: e.toString()));
DateTime? endTime; }
List<TemporaryPassword>? temporaryPasswords = []; }
List<TemporaryPassword>? oneTimePasswords = [];
void getTimeLimitPasswords(InitialTimeLimitPassword event, Emitter<SmartDoorState> 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) {
emit(FailedState(errorMessage: e.toString()));
}
}
changeTime(ChangeTimeEvent event, Emitter<SmartDoorState> emit) { changeTime(ChangeTimeEvent event, Emitter<SmartDoorState> emit) {
emit(LoadingInitialState());
if (event.isStartEndTime == true) { if (event.isStartEndTime == true) {
startTime = event.val; startTime = event.val;
} else { } else {
endTime = event.val; endTime = event.val;
} }
emit(ChangeTimeState());
} }
bool toggleRepeat(ToggleRepeatEvent event, Emitter<SmartDoorState> emit) { bool toggleRepeat(ToggleRepeatEvent event, Emitter<SmartDoorState> emit) {
emit(LoadingInitialState()); emit(LoadingInitialState());
repeat = !repeat; repeat = !repeat;
emit(IsRepeatState()); emit(IsRepeatState(repeat: repeat));
return repeat; return repeat;
} }
bool setStartEndTime(SetStartEndTimeEvent event, Emitter<SmartDoorState> emit) { bool setStartEndTime(SetStartEndTimeEvent event, Emitter<SmartDoorState> emit) {
emit(LoadingInitialState()); emit(LoadingInitialState());
isStartEndTime = event.val; isStartEndTime = event.val;
emit(IsStartEndState()); emit(IsStartEndState(isStartEndTime:isStartEndTime));
return isStartEndTime; return isStartEndTime;
} }
@ -119,16 +192,6 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
emit(UpdateState(smartDoorModel: deviceStatus)); emit(UpdateState(smartDoorModel: deviceStatus));
} }
void generate7DigitNumber(GeneratePasswordEvent event, Emitter<SmartDoorState> emit) async {
emit(LoadingInitialState());
passwordController.clear();
Random random = Random();
int min = 1000000;
int max = 9999999;
passwordController.text = (min + random.nextInt(max - min + 1)).toString();
emit(GeneratePasswordState());
}
Future<void> selectTime(SelectTimeEvent event, Emitter<SmartDoorState> emit) async { Future<void> selectTime(SelectTimeEvent event, Emitter<SmartDoorState> emit) async {
emit(ChangeTimeState()); emit(ChangeTimeState());
final DateTime? picked = await showDatePicker( final DateTime? picked = await showDatePicker(
@ -166,7 +229,6 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
timePicked.hour, timePicked.hour,
timePicked.minute, timePicked.minute,
); );
// Convert selectedDateTime to a timestamp without seconds and milliseconds
final selectedTimestamp = DateTime( final selectedTimestamp = DateTime(
selectedDateTime.year, selectedDateTime.year,
selectedDateTime.month, selectedDateTime.month,
@ -203,7 +265,6 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
isSavingPassword = true; isSavingPassword = true;
emit(LoadingSaveState()); emit(LoadingSaveState());
var res = await DevicesAPI.createPassword( var res = await DevicesAPI.createPassword(
pageType: pageType,
deviceId: deviceId, deviceId: deviceId,
effectiveTime: effectiveTimeTimeStamp.toString(), effectiveTime: effectiveTimeTimeStamp.toString(),
invalidTime: expirationTimeTimeStamp.toString(), invalidTime: expirationTimeTimeStamp.toString(),
@ -214,24 +275,48 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
Schedule( Schedule(
effectiveTime: getTimeOnly(startTime), effectiveTime: getTimeOnly(startTime),
invalidTime: getTimeOnly(endTime).toString(), invalidTime: getTimeOnly(endTime).toString(),
workingDay: selectedDay!, workingDay: selectedDays,
), ),
], ],
); );
Navigator.of(event.context).pop(true); Navigator.of(event.context).pop(true);
CustomSnackBar.displaySnackBar('Save Successfully'); CustomSnackBar.displaySnackBar('Save Successfully');
emit(SaveState()); emit(SaveState());
} catch (_) {}finally { } catch (_) {
}finally {
isSavingPassword = false; isSavingPassword = false;
} }
} }
Future<void> generateAndSavePasswordTimeLimited (GenerateAndSavePasswordTimeLimitEvent event, Emitter<SmartDoorState> emit) async {
if (timeLimitValidate() || isSavingPassword) return;
try {
isSavingPassword = true;
emit(LoadingInitialState());
var res = await DevicesAPI.generateMultiTimePassword(deviceId: deviceId,
effectiveTime: effectiveTimeTimeStamp.toString(),
invalidTime: expirationTimeTimeStamp.toString(), );
ApiResponse pass= ApiResponse.fromJson(res);
passwordController.text =pass.data.offlineTempPassword;
passwordId=pass.data.offlineTempPasswordId;
CustomSnackBar.displaySnackBar('Save Successfully');
emit(const GeneratePasswordOneTimestate(generated: true));
} catch (_) {
emit(FailedState(errorMessage: e.toString()));
}
finally {
isSavingPassword = false;
}
}
Future<void> deletePassword(DeletePasswordEvent event, Emitter<SmartDoorState> emit) async { Future<void> deletePassword(DeletePasswordEvent event, Emitter<SmartDoorState> emit) async {
try { try {
emit(LoadingInitialState()); emit(LoadingInitialState());
var response = var response =
await DevicesAPI.deletePassword(deviceId: deviceId, passwordId: event.passwordId) await DevicesAPI.deletePassword(deviceId: deviceId, passwordId: event.passwordId)
.then((value) async { .then((value) async {
add(InitialPasswordsPage(type: pageType)); add(InitialPasswordsPage());
}); });
} catch (e) { } catch (e) {
emit(FailedState(errorMessage: e.toString())); emit(FailedState(errorMessage: e.toString()));
@ -239,7 +324,7 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
} }
bool _validateInputs() { bool _validateInputs() {
if (passwordController.text.length < 7) { if (passwordController.text.length < 7 ) {
CustomSnackBar.displaySnackBar('Password less than 7'); CustomSnackBar.displaySnackBar('Password less than 7');
return true; return true;
} }
@ -247,7 +332,7 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
CustomSnackBar.displaySnackBar('Password required'); CustomSnackBar.displaySnackBar('Password required');
return true; return true;
} }
if (passwordNameController.text.isEmpty) { if (passwordNameController.text.isEmpty ) {
CustomSnackBar.displaySnackBar('Password name required'); CustomSnackBar.displaySnackBar('Password name required');
return true; return true;
} }
@ -259,25 +344,51 @@ class SmartDoorBloc extends Bloc<SmartDoorEvent, SmartDoorState> {
CustomSnackBar.displaySnackBar('Select expiration time'); CustomSnackBar.displaySnackBar('Select expiration time');
return true; return true;
} }
if (repeat == true && (endTime == null || startTime == null || selectedDay == null)) { if (repeat == true && (endTime == null || startTime == null || selectedDays == null)) {
CustomSnackBar.displaySnackBar('Start Time and End time and the days required '); CustomSnackBar.displaySnackBar('Start Time and End time and the days required ');
return true; return true;
} }
return false; return false;
} }
bool timeLimitValidate() {
List<DayInWeek> days = [ if (effectiveTime == 'Select Time' || effectiveTimeTimeStamp == null) {
DayInWeek("S", dayKey: 'Sun'), CustomSnackBar.displaySnackBar('Select effective time');
DayInWeek("M", dayKey: 'Mon'), return true;
DayInWeek("T", dayKey: 'Tue'), }
DayInWeek("W", dayKey: 'Wed'), if (expirationTime == 'Select Time' || expirationTimeTimeStamp == null) {
DayInWeek("T", dayKey: 'Thu'), CustomSnackBar.displaySnackBar('Select expiration time');
DayInWeek("F", dayKey: 'Fri'), return true;
DayInWeek("S", dayKey: 'Sat'), }
return false;
}
List<Map<String, String>> days = [
{"day": "Sun", "key": "Sun"},
{"day": "Mon", "key": "Mon"},
{"day": "Tue", "key": "Tue"},
{"day": "Wed", "key": "Wed"},
{"day": "Thu", "key": "Thu"},
{"day": "Fri", "key": "Fri"},
{"day": "Sat", "key": "Sat"},
]; ];
List<String> selectedDays = [];
Future<void> toggleDaySelection(ToggleDaySelectionEvent event, Emitter<SmartDoorState> emit,)async {
emit(LoadingInitialState());
if (selectedDays.contains(event.key)) {
selectedDays.remove(event.key);
} else {
selectedDays.add(event.key);
}
emit(ChangeTimeState());
}
String getTimeOnly(DateTime? dateTime) { String getTimeOnly(DateTime? dateTime) {
if (dateTime == null) return ''; if (dateTime == null) return '';
return DateFormat('HH:mm').format(dateTime); return DateFormat('HH:mm').format(dateTime);
} }
} }

View File

@ -9,11 +9,9 @@ abstract class SmartDoorEvent extends Equatable {
} }
class InitialEvent extends SmartDoorEvent {} class InitialEvent extends SmartDoorEvent {}
class InitialPasswordsPage extends SmartDoorEvent { class InitialPasswordsPage extends SmartDoorEvent {}
final String? type;
const InitialPasswordsPage({ this.type});
}
class InitialOneTimePassword extends SmartDoorEvent {} class InitialOneTimePassword extends SmartDoorEvent {}
class InitialTimeLimitPassword extends SmartDoorEvent {}
class UpdateLockEvent extends SmartDoorEvent { class UpdateLockEvent extends SmartDoorEvent {
final bool value; final bool value;
@ -27,9 +25,26 @@ class SavePasswordEvent extends SmartDoorEvent {
const SavePasswordEvent({required this.context}); const SavePasswordEvent({required this.context});
@override @override
List<Object> get props => [context]; List<Object> get props => [context];
}
class GenerateAndSavePasswordTimeLimitEvent extends SmartDoorEvent {
final BuildContext context;
const GenerateAndSavePasswordTimeLimitEvent({required this.context});
@override
List<Object> get props => [context];
}
class GenerateAndSavePasswordOneTimeEvent extends SmartDoorEvent {
final BuildContext context;
const GenerateAndSavePasswordOneTimeEvent({required this.context});
@override
List<Object> get props => [context];
}
class GeneratePasswordEvent extends SmartDoorEvent {
} }
class GeneratePasswordEvent extends SmartDoorEvent {}
class SelectTimeEvent extends SmartDoorEvent { class SelectTimeEvent extends SmartDoorEvent {
final BuildContext context; final BuildContext context;
final bool isEffective; final bool isEffective;
@ -41,7 +56,6 @@ class SelectTimeEvent extends SmartDoorEvent {
class ToggleRepeatEvent extends SmartDoorEvent {} class ToggleRepeatEvent extends SmartDoorEvent {}
class SetStartEndTimeEvent extends SmartDoorEvent { class SetStartEndTimeEvent extends SmartDoorEvent {
final bool val; final bool val;
const SetStartEndTimeEvent({required this.val}); const SetStartEndTimeEvent({required this.val});
@override @override
List<Object> get props => [val]; List<Object> get props => [val];
@ -55,6 +69,14 @@ class DeletePasswordEvent extends SmartDoorEvent {
List<Object> get props => [passwordId]; List<Object> get props => [passwordId];
} }
class ToggleDaySelectionEvent extends SmartDoorEvent {
final String key;
const ToggleDaySelectionEvent({required this.key});
@override
List<Object> get props => [key];
}
class ChangeTimeEvent extends SmartDoorEvent { class ChangeTimeEvent extends SmartDoorEvent {
final dynamic val; final dynamic val;
final bool isStartEndTime; final bool isStartEndTime;
@ -64,4 +86,7 @@ class ChangeTimeEvent extends SmartDoorEvent {
List<Object> get props => [val,isStartEndTime]; List<Object> get props => [val,isStartEndTime];
} }
class RenamePasswordEvent extends SmartDoorEvent {
}

View File

@ -38,13 +38,27 @@ class FailedState extends SmartDoorState {
List<Object> get props => [errorMessage]; List<Object> get props => [errorMessage];
} }
class GeneratePasswordState extends SmartDoorState {} class GeneratePasswordState extends SmartDoorState {
}
class TimeSelectedState extends SmartDoorState {} class TimeSelectedState extends SmartDoorState {}
class IsRepeatState extends SmartDoorState {} class IsRepeatState extends SmartDoorState {
final bool repeat;
const IsRepeatState({required this.repeat});
class IsStartEndState extends SmartDoorState {} @override
List<Object> get props => [repeat];
}
class IsStartEndState extends SmartDoorState {
final bool isStartEndTime;
const IsStartEndState({required this.isStartEndTime});
@override
List<Object> get props => [isStartEndTime];
}
class ChangeStartTimeState extends SmartDoorState {} class ChangeStartTimeState extends SmartDoorState {}
@ -56,6 +70,12 @@ class SaveState extends SmartDoorState {}
class LoadingSaveState extends SmartDoorState {} class LoadingSaveState extends SmartDoorState {}
class GeneratePasswordOneTimestate extends SmartDoorState {
final bool generated;
const GeneratePasswordOneTimestate({required this.generated});
List<Object> get props => [generated];
}
class TemporaryPasswordsLoadedState extends SmartDoorState { class TemporaryPasswordsLoadedState extends SmartDoorState {
final List<TemporaryPassword> temporaryPassword; final List<TemporaryPassword> temporaryPassword;
const TemporaryPasswordsLoadedState({required this.temporaryPassword}); const TemporaryPasswordsLoadedState({required this.temporaryPassword});

View File

@ -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<String, dynamic> 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<String, dynamic> toJson() {
return {
'gmtCreate': gmtCreate,
'gmtExpired': gmtExpired,
'gmtStart': gmtStart,
'hasClearPwd': hasClearPwd,
'optUid': optUid,
'pwd': pwd,
'pwdId': pwdId,
'pwdName': pwdName,
'pwdTypeCode': pwdTypeCode,
'revokedPwdName': revokedPwdName,
'status': status,
};
}
}

View File

@ -0,0 +1,69 @@
class OfflineTemporaryPassword {
dynamic effectiveTime;
dynamic invalidTime;
dynamic offlineTempPassword;
dynamic offlineTempPasswordId;
dynamic offlineTempPasswordName;
OfflineTemporaryPassword({
required this.effectiveTime,
required this.invalidTime,
required this.offlineTempPassword,
required this.offlineTempPasswordId,
required this.offlineTempPasswordName,
});
factory OfflineTemporaryPassword.fromJson(Map<String, dynamic> json) {
return OfflineTemporaryPassword(
effectiveTime: json['effective_time'],
invalidTime: json['invalid_time'],
offlineTempPassword: json['offline_temp_password'],
offlineTempPasswordId: json['offline_temp_password_id'],
offlineTempPasswordName: json['offline_temp_password_name'],
);
}
Map<String, dynamic> toJson() {
return {
'effective_time': effectiveTime,
'invalid_time': invalidTime,
'offline_temp_password': offlineTempPassword,
'offline_temp_password_id': offlineTempPasswordId,
'offline_temp_password_name': offlineTempPasswordName,
};
}
}
class ApiResponse {
int statusCode;
bool success;
String message;
OfflineTemporaryPassword data;
ApiResponse({
required this.statusCode,
required this.success,
required this.message,
required this.data,
});
factory ApiResponse.fromJson(Map<String, dynamic> json) {
return ApiResponse(
statusCode: json['statusCode'],
success: json['success'],
message: json['message'],
data: OfflineTemporaryPassword.fromJson(json['data']['result']),
);
}
Map<String, dynamic> toJson() {
return {
'statusCode': statusCode,
'success': success,
'message': message,
'data': {
'result': data.toJson(),
},
};
}
}

View File

@ -0,0 +1,116 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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/shared_widgets/default_container.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class NameTimeWidget extends StatelessWidget {
const NameTimeWidget({super.key});
@override
Widget build(BuildContext context) {
return DefaultContainer(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(10.0),
child: const BodyMedium(
text: 'Password Name',
fontWeight: FontWeight.normal,
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 2.6,
child: TextFormField(
controller: BlocProvider.of<SmartDoorBloc>(context)
.passwordNameController,
decoration: const InputDecoration(
hintText: 'Enter The Name',
hintStyle: TextStyle(
fontSize: 14, color: ColorsManager.textGray)),
)),
],
),
Column(
children: [
const Divider(
color: ColorsManager.graysColor,
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Expanded(
child: BodyMedium(
text: 'Effective Time',
fontWeight: FontWeight.normal,
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 3.5,
child: InkWell(
onTap: () {
BlocProvider.of<SmartDoorBloc>(context).add(SelectTimeEvent(context: context, isEffective: true));
},
child: Text(
BlocProvider.of<SmartDoorBloc>(context).effectiveTime,
style: TextStyle(fontSize: 14,
color: BlocProvider.of<SmartDoorBloc>(context).effectiveTime ==
'Select Time'
? ColorsManager.textGray
: null),
),
)),
],),
),
const Divider(
color: ColorsManager.graysColor,
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Expanded(
child: BodyMedium(
text: 'Expiration Time',
fontWeight: FontWeight.normal,
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 3.5,
child: InkWell(
onTap: () {
BlocProvider.of<SmartDoorBloc>(context).add(
SelectTimeEvent(
context: context, isEffective: false));
},
child: Text(
BlocProvider.of<SmartDoorBloc>(context).expirationTime,
style: TextStyle(
fontSize: 14,
color: BlocProvider.of<SmartDoorBloc>(context).expirationTime == 'Select Time'
? ColorsManager.textGray
: null),
),
),
),
],
),
),
],
),
],
));
}
}

View File

@ -0,0 +1,228 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.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/repeat_widget.dart';
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;
const OfflineOneTimePasswordPage({super.key, this.deviceId, this.type});
@override
Widget build(BuildContext context) {
bool isRepeat = false;
bool generated = false;
return BlocProvider(
create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!),
child: BlocConsumer<SmartDoorBloc, SmartDoorState>(listener: (context, state) {
if (state is FailedState) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.errorMessage),
backgroundColor: Colors.red,
),
);
}
if (state is IsRepeatState){
isRepeat = state.repeat;
}
if (state is GeneratePasswordOneTimestate ){
generated = state.generated;
}
}, builder: (context, state) {
final smartDoorBloc = BlocProvider.of<SmartDoorBloc>(context);
return DefaultScaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
centerTitle: true,
title: const BodyLarge(
text: 'Create One-Time Password',
fontColor: ColorsManager.primaryColor,
fontWeight: FontsManager.bold,
),
leading: IconButton(
onPressed: () {
Navigator.of(context).pop(true);
},
icon: const Icon(Icons.arrow_back)
),
),
child: state is LoadingInitialState
? const Center(child: CircularProgressIndicator())
: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const BodyMedium(
text: 'Save the password immediately. The password is not displayed in the app.',
fontWeight: FontWeight.normal,
fontColor: ColorsManager.grayColor,
),
const SizedBox(
height: 20,
),
const BodyMedium(
text: '7-Digit Password',
fontWeight: FontWeight.normal,
fontColor: ColorsManager.grayColor,
),
DefaultContainer(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: [
Flexible(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children:smartDoorBloc.passwordController.text.isEmpty?
List.generate(10, (index) {
return const Padding(
padding: EdgeInsets.symmetric(horizontal: 4.0,vertical: 15),
child: Icon(
Icons.circle,
size: 20.0,
color: Colors.black,
),
);
}) :[
Expanded(
child: Row(
children: [
Expanded(
child: BodyLarge(
style: const TextStyle(
color: ColorsManager.primaryColor,
fontWeight: FontWeight.bold,
letterSpacing: 8.0 ,
fontSize: 25,
wordSpacing: 2),
textAlign: TextAlign.center,
text: smartDoorBloc.passwordController.text,
fontSize: 25,
),),
IconButton(
onPressed: () async {
await Clipboard.setData(ClipboardData(
text: smartDoorBloc.passwordController.text));
},
icon: const Icon(Icons.copy)
),
],
),
),
],
)),
const SizedBox(
width: 10,
),
],
),
if(smartDoorBloc.passwordController.text.isNotEmpty)
Column(
children: [
const Divider(
color: ColorsManager.graysColor,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(10.0),
child: const BodyMedium(
text: 'Password Name',
fontWeight: FontWeight.normal,
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 2.6,
child: TextFormField(
controller: BlocProvider.of<SmartDoorBloc>(context).passwordNameController,
decoration: const InputDecoration(
hintText: 'Enter The Name',
hintStyle: TextStyle(
fontSize: 14, color: ColorsManager.textGray)),
)),
],
),
],
),
],
),
),
const SizedBox(
height: 20,
),
const BodyMedium(
textAlign: TextAlign.center,
text: 'Save the password immediately. The password is not displayed in the app.',
fontWeight: FontWeight.normal,
fontColor: ColorsManager.grayColor,
),
// NameTimeWidget(type:type!),
const SizedBox(
height: 20,
),
Center(
child: SizedBox(
width: MediaQuery.of(context).size.width/1.5,
child: DoorLockButton(
isDone: generated,
isLoading: smartDoorBloc.isSavingPassword ,
borderRadius: 30,
backgroundColor:ColorsManager.primaryColor ,
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.of(context).pop(true);
}
},
child: const BodyMedium(
text: 'Obtain Password',
fontWeight: FontWeight.bold,
fontColor: Colors.white,
),),
),
),
const SizedBox(
height: 20,
),
isRepeat? const RepeatWidget():const SizedBox(),
const SizedBox(
height: 40,
)
],
),
),
);
}));
}
}

View File

@ -1,4 +1,3 @@
import 'package:day_picker/day_picker.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -7,6 +6,8 @@ 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_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_event.dart';
import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart'; import 'package:syncrow_app/features/devices/bloc/smart_door_bloc/smart_door_state.dart';
import 'package:syncrow_app/features/devices/view/widgets/name_time_widget.dart';
import 'package:syncrow_app/features/devices/view/widgets/smart_door/repeat_widget.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.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/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_large.dart';
@ -14,15 +15,15 @@ import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dar
import 'package:syncrow_app/utils/helpers/snack_bar.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/color_manager.dart';
import 'package:syncrow_app/utils/resource_manager/font_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 { class CreateTemporaryPassword extends StatelessWidget {
final String? deviceId; final String? deviceId;
final String? type; final String? type;
const CreateTemporaryPassword({super.key, this.deviceId, this.type}); const CreateTemporaryPassword({super.key, this.deviceId, this.type});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isRepeat = false;
bool generated = false;
return BlocProvider( return BlocProvider(
create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!), create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!),
child: BlocConsumer<SmartDoorBloc, SmartDoorState>(listener: (context, state) { child: BlocConsumer<SmartDoorBloc, SmartDoorState>(listener: (context, state) {
@ -34,7 +35,14 @@ class CreateTemporaryPassword extends StatelessWidget {
), ),
); );
} }
if (state is IsRepeatState){
isRepeat = state.repeat;
}
if (state is GeneratePasswordOneTimestate ){
generated = state.generated;
}
}, builder: (context, state) { }, builder: (context, state) {
final smartDoorBloc = BlocProvider.of<SmartDoorBloc>(context);
return DefaultScaffold( return DefaultScaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
@ -46,19 +54,19 @@ class CreateTemporaryPassword extends StatelessWidget {
), ),
leading: IconButton( leading: IconButton(
onPressed: () { onPressed: () {
Navigator.of(context).pop('UpdatePage'); Navigator.of(context).pop(true);
}, },
icon: const Icon(Icons.arrow_back) icon: const Icon(Icons.arrow_back)
), ),
actions: [ actions:
type == 'Online Password'?[
TextButton( TextButton(
onPressed: () { onPressed: () {
BlocProvider.of<SmartDoorBloc>(context) smartDoorBloc.add(SavePasswordEvent(context: context));
.add(SavePasswordEvent(context: context));
}, },
child: const Text('Save') child: const Text('Save')
) )
], ]:null,
), ),
child: state is LoadingInitialState child: state is LoadingInitialState
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
@ -82,6 +90,8 @@ class CreateTemporaryPassword extends StatelessWidget {
), ),
DefaultContainer( DefaultContainer(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15), padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: type == 'Online Password'?0:25),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
@ -91,14 +101,14 @@ class CreateTemporaryPassword extends StatelessWidget {
child: PinCodeTextField( child: PinCodeTextField(
onCompleted: (value) { onCompleted: (value) {
if (value.split('').every((char) => char == '1')) { if (value.split('').every((char) => char == '1')) {
BlocProvider.of<SmartDoorBloc>(context).passwordController.clear(); smartDoorBloc.passwordController.clear();
CustomSnackBar.displaySnackBar('All characters cannot be 1.'); CustomSnackBar.displaySnackBar('All characters cannot be 1.');
} }
}, },
autoDisposeControllers: false, autoDisposeControllers: false,
keyboardType: TextInputType.phone, keyboardType: TextInputType.phone,
length: 7, length: 7,
enabled: true, // enabled:type == 'Online Password'? true:false,
obscureText: false, obscureText: false,
animationType: AnimationType.fade, animationType: AnimationType.fade,
pinTheme: PinTheme( pinTheme: PinTheme(
@ -115,17 +125,17 @@ class CreateTemporaryPassword extends StatelessWidget {
animationDuration: const Duration(milliseconds: 300), animationDuration: const Duration(milliseconds: 300),
backgroundColor: Colors.white, backgroundColor: Colors.white,
enableActiveFill: false, enableActiveFill: false,
controller: BlocProvider.of<SmartDoorBloc>(context).passwordController, controller: smartDoorBloc.passwordController,
appContext: context, appContext: context,
)), )),
const SizedBox( const SizedBox(
width: 10, width: 10,
), ),
if(type == 'Online Password')
Flexible( Flexible(
child: InkWell( child: InkWell(
onTap: () { onTap: () {
BlocProvider.of<SmartDoorBloc>(context) smartDoorBloc.add(GeneratePasswordEvent());
.add(GeneratePasswordEvent());
}, },
child: const BodyMedium( child: const BodyMedium(
text: 'Generate Randomly', text: 'Generate Randomly',
@ -136,126 +146,22 @@ class CreateTemporaryPassword extends StatelessWidget {
], ],
), ),
), ),
BlocProvider.of<SmartDoorBloc>(context).passwordController.text.isNotEmpty ),
? TextButton( if(smartDoorBloc.passwordController.text.isNotEmpty)
TextButton(
onPressed: () async { onPressed: () async {
await Clipboard.setData(ClipboardData( await Clipboard.setData(ClipboardData(
text: BlocProvider.of<SmartDoorBloc>(context) text: smartDoorBloc.passwordController.text));
.passwordController
.text));
}, },
child: const Text('Copy')) child: const Text('Copy')
: const SizedBox(), ),
const SizedBox( const SizedBox(
height: 20, height: 20,
), ),
DefaultContainer( NameTimeWidget(),
padding: const EdgeInsets.all(20),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(10.0),
child: const BodyMedium(
text: 'Password Name',
fontWeight: FontWeight.normal,
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 2.6,
child: TextFormField(
controller: BlocProvider.of<SmartDoorBloc>(context)
.passwordNameController,
decoration: const InputDecoration(
hintText: 'Enter The Name',
hintStyle: TextStyle(
fontSize: 14, color: ColorsManager.textGray)),
)),
],
),
const Divider(
color: ColorsManager.graysColor,
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Expanded(
child: BodyMedium(
text: 'Effective Time',
fontWeight: FontWeight.normal,
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 3.5,
child: InkWell(
onTap: () {
BlocProvider.of<SmartDoorBloc>(context).add(
SelectTimeEvent(
context: context, isEffective: true));
},
child: Text(
BlocProvider.of<SmartDoorBloc>(context).effectiveTime,
style: TextStyle(
fontSize: 14,
color: BlocProvider.of<SmartDoorBloc>(context)
.effectiveTime ==
'Select Time'
? ColorsManager.textGray
: null),
),
)),
],
),
),
const Divider(
color: ColorsManager.graysColor,
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Expanded(
child: BodyMedium(
text: 'Expiration Time',
fontWeight: FontWeight.normal,
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 3.5,
child: InkWell(
onTap: () {
BlocProvider.of<SmartDoorBloc>(context).add(
SelectTimeEvent(
context: context, isEffective: false));
},
child: Text(
BlocProvider.of<SmartDoorBloc>(context).expirationTime,
style: TextStyle(
fontSize: 14,
color: BlocProvider.of<SmartDoorBloc>(context)
.expirationTime ==
'Select Time'
? ColorsManager.textGray
: null),
),
),
),
],
),
),
],
)),
const SizedBox( const SizedBox(
height: 20, height: 20,
), ),
if (type == 'Online Password')
DefaultContainer( DefaultContainer(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: ListTile( child: ListTile(
@ -267,133 +173,18 @@ class CreateTemporaryPassword extends StatelessWidget {
trailing: Transform.scale( trailing: Transform.scale(
scale: .8, scale: .8,
child: CupertinoSwitch( child: CupertinoSwitch(
value: BlocProvider.of<SmartDoorBloc>(context).repeat, value: smartDoorBloc.repeat,
onChanged: (value) { onChanged: (value) {
BlocProvider.of<SmartDoorBloc>(context) smartDoorBloc.add(ToggleRepeatEvent());
.add(ToggleRepeatEvent());
}, },
applyTheme: true, applyTheme: true,
)), )),
), ),
), ) ,
const SizedBox( const SizedBox(
height: 20, height: 20,
), ),
BlocProvider.of<SmartDoorBloc>(context).repeat isRepeat? const RepeatWidget():const SizedBox(),
? DefaultContainer(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () {
BlocProvider.of<SmartDoorBloc>(context)
.add(const SetStartEndTimeEvent(val: true));
},
child: BodyMedium(
text: 'Start',
fontColor: BlocProvider.of<SmartDoorBloc>(context)
.isStartEndTime ==
false
? Colors.black
: Colors.blue,
fontSize: 18,
),
),
InkWell(
onTap: () {
BlocProvider.of<SmartDoorBloc>(context)
.add(const SetStartEndTimeEvent(val: false));
},
child: BodyMedium(
text: 'End',
fontColor: BlocProvider.of<SmartDoorBloc>(context)
.isStartEndTime
? Colors.black
: Colors.blue,
fontSize: 18,
),
)
],
),
),
const Divider(
color: ColorsManager.graysColor,
),
Container(
height: 110,
child: BlocProvider.of<SmartDoorBloc>(context).isStartEndTime
? TimePickerSpinner(
time:
BlocProvider.of<SmartDoorBloc>(context).startTime,
is24HourMode: false,
itemHeight: 40,
normalTextStyle: const TextStyle(
color: Colors.grey,
fontSize: 24,
),
highlightedTextStyle:
const TextStyle(fontSize: 30, color: Colors.blue),
onTimeChange: (time) {
BlocProvider.of<SmartDoorBloc>(context).add(
ChangeTimeEvent(
val: time,
isStartEndTime:
BlocProvider.of<SmartDoorBloc>(context)
.isStartEndTime));
},
)
: Container(
child: TimePickerSpinner(
time:
BlocProvider.of<SmartDoorBloc>(context).endTime,
is24HourMode: false,
itemHeight: 40,
normalTextStyle: const TextStyle(
color: Colors.grey,
fontSize: 24,
),
highlightedTextStyle: const TextStyle(
fontSize: 30, color: Colors.blue),
onTimeChange: (time) {
BlocProvider.of<SmartDoorBloc>(context).add(
ChangeTimeEvent(
val: time,
isStartEndTime:
BlocProvider.of<SmartDoorBloc>(
context)
.isStartEndTime));
},
),
),
),
const Divider(
color: ColorsManager.graysColor,
),
const SizedBox(height: 20),
SelectWeekDays(
width: MediaQuery.of(context).size.width / 1,
fontSize: 18,
fontWeight: FontWeight.w600,
days: BlocProvider.of<SmartDoorBloc>(context).days,
border: false,
selectedDayTextColor: Colors.black,
unSelectedDayTextColor: Colors.grey,
boxDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
color: Colors.white),
onSelect: (values) {
BlocProvider.of<SmartDoorBloc>(context).selectedDay =
values;
},
),
],
))
: const SizedBox(),
const SizedBox( const SizedBox(
height: 40, height: 40,
) )

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.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/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/body_medium.dart';
import 'package:syncrow_app/utils/context_extension.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'; import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
class DoorDialog extends StatefulWidget { class DoorDialog extends StatefulWidget {
final String title; final String? title;
final TemporaryPassword value; final TemporaryPassword? temporaryPassword;
final OfflinePasswordModel? offline;
const DoorDialog({ const DoorDialog({
super.key, super.key,
required this.title, this.title,
required this.value, this.offline,
this.temporaryPassword,
}); });
@override @override
@ -28,16 +31,18 @@ class DoorDialogState extends State<DoorDialog> {
@override @override
Widget build(BuildContext context) { 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 = 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 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 = 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 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( return Dialog(
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
@ -49,7 +54,7 @@ class DoorDialogState extends State<DoorDialog> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
BodyMedium( BodyMedium(
text: widget.title, text: widget.title ?? '',
style: context.bodyMedium.copyWith( style: context.bodyMedium.copyWith(
color: ColorsManager.primaryColorWithOpacity, color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontsManager.extraBold, fontWeight: FontsManager.extraBold,
@ -111,6 +116,7 @@ class DoorDialogState extends State<DoorDialog> {
width: double.infinity, width: double.infinity,
color: ColorsManager.greyColor, color: ColorsManager.greyColor,
), ),
widget.temporaryPassword?.effectiveTime!=null?
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
@ -142,6 +148,27 @@ class DoorDialogState extends State<DoorDialog> {
), ),
), ),
], ],
):
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),
),
),
),
),
],
) )
], ],
), ),

View File

@ -0,0 +1,295 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.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/repeat_widget.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 CreateOfflineTimeLimitPasswordPage extends StatelessWidget {
final String? deviceId;
final String? type;
const CreateOfflineTimeLimitPasswordPage({super.key, this.deviceId, this.type});
@override
Widget build(BuildContext context) {
bool isRepeat = false;
bool generated = false;
return BlocProvider(
create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!),
child: BlocConsumer<SmartDoorBloc, SmartDoorState>(
listener: (context, state) {
if (state is FailedState) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.errorMessage),
backgroundColor: Colors.red,
),
);
}
if (state is IsRepeatState) {
isRepeat = state.repeat;
}
if (state is GeneratePasswordOneTimestate) {
generated = state.generated;
}
}, builder: (context, state) {
final smartDoorBloc = BlocProvider.of<SmartDoorBloc>(context);
return DefaultScaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
centerTitle: true,
title: const BodyLarge(
text: 'Create Time-Limited Password',
fontColor: ColorsManager.primaryColor,
fontWeight: FontsManager.bold,
),
leading: IconButton(
onPressed: () {
Navigator.of(context).pop(true);
},
icon: const Icon(Icons.arrow_back)),
),
child: state is LoadingInitialState
? const Center(child: CircularProgressIndicator())
: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const BodyMedium(
text:
'Save the password immediately. The password is not displayed in the app.',
fontWeight: FontWeight.normal,
fontColor: ColorsManager.grayColor,
),
const SizedBox(
height: 20,
),
DefaultContainer(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 15),
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: [
Flexible(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: smartDoorBloc.passwordController.text.isEmpty ?
List.generate(10, (index) {
return const Padding(
padding: EdgeInsets.symmetric(
horizontal: 4.0,
vertical: 15),
child: Icon(
Icons.circle,
size: 20.0,
color: Colors.black,
),
);
}) : [
Expanded(
child: Row(
children: [
Expanded(
child: BodyLarge(
style: const TextStyle(
color: ColorsManager.primaryColor,
fontWeight: FontWeight.bold,
letterSpacing: 8.0,
fontSize: 25,
wordSpacing: 2),
textAlign: TextAlign.center,
text: smartDoorBloc.passwordController.text,
fontSize: 25,
),
),
IconButton(
onPressed: () async {
await Clipboard.setData(
ClipboardData(text: smartDoorBloc.passwordController.text)
);
},
icon: const Icon(Icons.copy)),
],
),
),
],
)),
const SizedBox(
width: 10,
),
],
),
DefaultContainer(
padding: const EdgeInsets.all(20),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Container(
padding: const EdgeInsets.all(10.0),
child: const BodyMedium(
text: 'Password Name',
fontWeight: FontWeight.normal,
),
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 2.6,
child: TextFormField(
controller: BlocProvider.of<SmartDoorBloc>(context).passwordNameController,
decoration:
const InputDecoration(
hintText: 'Enter The Name',
hintStyle: TextStyle(
fontSize: 14,
color: ColorsManager.textGray)
),
)),
],
),
Column(
children: [
const Divider(color: ColorsManager.graysColor,),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Expanded(
child: BodyMedium(
text: 'Effective Time',
fontWeight: FontWeight.normal,
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 3.5,
child: InkWell(
onTap: () {
BlocProvider.of<SmartDoorBloc>(context).add(SelectTimeEvent(context: context, isEffective: true));
},
child: Text(
BlocProvider.of<SmartDoorBloc>(context).effectiveTime,
style: TextStyle(
fontSize: 14,
color: BlocProvider.of<SmartDoorBloc>(context).effectiveTime ==
'Select Time' ? ColorsManager.textGray : null),
),
)),],
),
),
const Divider(
color: ColorsManager.graysColor,
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Row(mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
const Expanded(
child: BodyMedium(
text: 'Expiration Time',
fontWeight: FontWeight.normal,
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 3.5,
child: InkWell(
onTap: () {
BlocProvider.of<SmartDoorBloc>(context).add(SelectTimeEvent(
context: context,
isEffective: false));
},
child: Text(
BlocProvider.of<SmartDoorBloc>(context).expirationTime,
style: TextStyle(
fontSize: 14,
color: BlocProvider.of<SmartDoorBloc>(context)
.expirationTime == 'Select Time' ? ColorsManager
.textGray : null),
),
),
),
],
),
),
],
),
],
)),
],
),
),
const SizedBox(
height: 20,
),
const BodyMedium(
textAlign: TextAlign.center,
text:
'Use the time-limited password at least once within 24 hours after the password takes effect. Otherwise, the password becomes invalid.',
fontWeight: FontWeight.normal,
fontColor: ColorsManager.grayColor,
),
// NameTimeWidget(type:type!),
const SizedBox(
height: 20,
),
Center(
child: SizedBox(
width: MediaQuery.of(context).size.width / 1.5,
child: DoorLockButton(
isDone: generated,
isLoading: smartDoorBloc.isSavingPassword,
borderRadius: 30,
backgroundColor: ColorsManager.primaryColor,
onPressed: () async {
if (generated == false) {
smartDoorBloc.add(
GenerateAndSavePasswordTimeLimitEvent(context: context));
await Clipboard.setData(
ClipboardData(text: smartDoorBloc.passwordController.text)
);
} else {
if(smartDoorBloc.passwordNameController.text.isNotEmpty){
smartDoorBloc.add(RenamePasswordEvent());
}
Navigator.of(context).pop(true);
}
},
child: const BodyMedium(
text: 'Obtain Password',
fontWeight: FontWeight.bold,
fontColor: Colors.white,
),
),
),
),
const SizedBox(
height: 20,
),
isRepeat ? const RepeatWidget() : const SizedBox(),
const SizedBox(
height: 40,
)
],
),
),
);
}));
}
}

View File

@ -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<SmartDoorBloc, SmartDoorState>(
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<SmartDoorBloc>(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')
],
),
);
},
));
})
);
}
}

View File

@ -0,0 +1,125 @@
import 'package:flutter/cupertino.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_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/text_widgets/body_medium.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class RepeatWidget extends StatelessWidget {
const RepeatWidget({
super.key,
});
@override
Widget build(BuildContext context) {
return BlocBuilder<SmartDoorBloc, SmartDoorState>(
builder: (context, state) {
final smartDoorBloc = BlocProvider.of<SmartDoorBloc>(context);
return DefaultContainer(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: Column(children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWell(
onTap: () {
smartDoorBloc.add(const SetStartEndTimeEvent(val: true));
},
child: BodyMedium(
text: 'Start',
fontColor: smartDoorBloc.isStartEndTime == false
? Colors.black
: Colors.blue,
fontSize: 18,
),
),
InkWell(
onTap: () {
smartDoorBloc.add(const SetStartEndTimeEvent(val: false));
},
child: BodyMedium(
text: 'End',
fontColor: smartDoorBloc.isStartEndTime
? Colors.black
: Colors.blue,
fontSize: 18,
),
)
],
),
),
const Divider(
color: ColorsManager.graysColor,
),
smartDoorBloc.isStartEndTime
? Container(
height: 110,
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.time,
initialDateTime:smartDoorBloc.startTime,
onDateTimeChanged: (startTime) {
smartDoorBloc.add(ChangeTimeEvent(val: startTime, isStartEndTime: true));
},
)
):SizedBox(
height: 110,
child: CupertinoDatePicker(
mode: CupertinoDatePickerMode.time,
initialDateTime:smartDoorBloc.endTime,
onDateTimeChanged: (endTime) {
smartDoorBloc.add(ChangeTimeEvent(val: endTime, isStartEndTime: false));
},
)
),
const Divider(
color: ColorsManager.graysColor,
),
const SizedBox(height: 20),
SizedBox(
height: MediaQuery.of(context).size.height *0.10,
child: ListView(
scrollDirection: Axis.horizontal,
children: smartDoorBloc.days.map((day) {
bool isSelected = smartDoorBloc.selectedDays.contains(day['key']);
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
smartDoorBloc.add(ToggleDaySelectionEvent(key:day['key']!));
},
child: Container(
width: 70,
padding: EdgeInsets.symmetric(vertical: 8, horizontal: 8),
decoration: BoxDecoration(
border: Border.all(
color: isSelected?
Colors.black:ColorsManager.grayColor
),
color: Colors.transparent,
borderRadius: BorderRadius.circular(55),
),
child: Center(
child: Text(
day['day']!,
style: TextStyle(
fontSize: 18,
color: isSelected ? Colors.black : ColorsManager.grayColor,
),
),
),
),
),
);
}).toList(),
)
)
]));
});
}
}

View File

@ -1,6 +1,8 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.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/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_container.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart'; import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
@ -46,7 +48,11 @@ class TemporaryPasswordPage extends StatelessWidget {
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
onTap: () { onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => ViewTemporaryPassword(deviceId:deviceId,type:'Online Password'),)); Navigator.of(context).push(MaterialPageRoute(builder: (context) =>
ViewTemporaryPassword(
deviceId:deviceId,
type:'Online Password'),
));
}, },
trailing: const Icon( trailing: const Icon(
Icons.arrow_forward_ios, Icons.arrow_forward_ios,
@ -57,7 +63,6 @@ class TemporaryPasswordPage extends StatelessWidget {
), ),
], ],
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
Column( Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
@ -76,7 +81,8 @@ class TemporaryPasswordPage extends StatelessWidget {
const SizedBox(height: 10), const SizedBox(height: 10),
DefaultContainer( DefaultContainer(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20), padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 20),
child: Column(children: [ child: Column(
children: [
ListTile( ListTile(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
leading: SvgPicture.asset(Assets.oneTimePassword), leading: SvgPicture.asset(Assets.oneTimePassword),
@ -86,8 +92,7 @@ class TemporaryPasswordPage extends StatelessWidget {
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
onTap: () { 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( trailing: const Icon(
Icons.arrow_forward_ios, Icons.arrow_forward_ios,
@ -95,7 +100,6 @@ class TemporaryPasswordPage extends StatelessWidget {
size: 15, size: 15,
), ),
), ),
const Divider(color:ColorsManager.graysColor,), const Divider(color:ColorsManager.graysColor,),
ListTile( ListTile(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
@ -106,7 +110,7 @@ class TemporaryPasswordPage extends StatelessWidget {
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
onTap: () { 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( trailing: const Icon(
Icons.arrow_forward_ios, Icons.arrow_forward_ios,

View File

@ -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<SmartDoorBloc, SmartDoorState>(
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<SmartDoorBloc>(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')
],
),
);
},
));
})
);
}
}

View File

@ -20,8 +20,7 @@ class ViewTemporaryPassword extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (BuildContext context) => create: (BuildContext context) => SmartDoorBloc(deviceId: deviceId!)..add(InitialPasswordsPage( )),
SmartDoorBloc(deviceId: deviceId!)..add(InitialPasswordsPage(type:type )),
child: BlocConsumer<SmartDoorBloc, SmartDoorState>( child: BlocConsumer<SmartDoorBloc, SmartDoorState>(
listener: (context, state) { listener: (context, state) {
if (state is FailedState) { if (state is FailedState) {
@ -32,22 +31,25 @@ class ViewTemporaryPassword extends StatelessWidget {
), ),
); );
} }
}, builder: (context, state) { },
builder: (context, state) {
final smartDoorBloc = BlocProvider.of<SmartDoorBloc>(context); final smartDoorBloc = BlocProvider.of<SmartDoorBloc>(context);
return DefaultScaffold( return DefaultScaffold(
title: 'Passwords', title: 'Passwords',
actions: [ actions: [
IconButton( IconButton(
onPressed: () { onPressed: () {
Navigator.of(context).push(MaterialPageRoute( Navigator.of(context).push(
builder: (context) => CreateTemporaryPassword(deviceId: deviceId, type: type), MaterialPageRoute(builder: (context) =>
)).then((result) { CreateTemporaryPassword(deviceId: deviceId, type: type))).then((result) {
if (result != null && result) { if (result != null && result) {
smartDoorBloc.add(InitialPasswordsPage(type:type )); smartDoorBloc.add(InitialPasswordsPage());
smartDoorBloc.add(InitialPasswordsPage());
} }
}); });
}, },
icon: const Icon(Icons.add)) icon: const Icon(Icons.add)
)
], ],
child: Builder( child: Builder(
builder: (context) { builder: (context) {
@ -61,15 +63,13 @@ class ViewTemporaryPassword extends StatelessWidget {
return Padding( return Padding(
padding: const EdgeInsets.all(5.0), padding: const EdgeInsets.all(5.0),
child: DefaultContainer( child: DefaultContainer(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
horizontal: 15, vertical: 10),
child: ListTile( child: ListTile(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
leading: SvgPicture.asset( leading: SvgPicture.asset(
Assets.timeLimitedPasswordIcon), Assets.timeLimitedPasswordIcon),
title: BodyMedium( title: BodyMedium(
text: text: 'Password Name: ${smartDoorBloc.temporaryPasswords![index].name}',
'Password Name: ${smartDoorBloc.temporaryPasswords![index].name}',
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
onTap: () async { onTap: () async {
@ -78,7 +78,7 @@ class ViewTemporaryPassword extends StatelessWidget {
builder: (context) { builder: (context) {
return DoorDialog( return DoorDialog(
title: 'Password Information', title: 'Password Information',
value: smartDoorBloc.temporaryPasswords![index], temporaryPassword: smartDoorBloc.temporaryPasswords![index],
); );
}, },
); );
@ -110,6 +110,7 @@ class ViewTemporaryPassword extends StatelessWidget {
); );
}, },
)); ));
})); })
);
} }
} }

View File

@ -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<Color>(
(Set<MaterialState> 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 ,
),
),
);
}
}

View File

@ -160,15 +160,7 @@ abstract class ApiEndpoints {
static const String getOneTimeTemporaryPassword = static const String getOneTimeTemporaryPassword =
'/door-lock/temporary-password/offline/one-time/{doorLockUuid}'; '/door-lock/temporary-password/offline/one-time/{doorLockUuid}';
//multiple-time offline
static const String addMultipleTimeTemporaryPassword =
'/door-lock/temporary-password/offline/multiple-time/{doorLockUuid}';
static const String getMultipleTimeTemporaryPassword =
'/door-lock/temporary-password/offline/multiple-time/{doorLockUuid}';
//multiple-time offline
static const String deleteTemporaryPassword =
'/door-lock/temporary-password/{doorLockUuid}/{passwordId}';
//user //user
@ -179,4 +171,17 @@ abstract class ApiEndpoints {
static const String sendPicture = '/user/profile-picture/{userUuid}'; static const String sendPicture = '/user/profile-picture/{userUuid}';
static const String getRegion = '/region'; static const String getRegion = '/region';
static const String getTimezone = '/timezone'; static const String getTimezone = '/timezone';
//multiple-time offline
static const String addMultipleTimeTemporaryPassword =
'/door-lock/temporary-password/offline/multiple-time/{doorLockUuid}';
static const String getMultipleTimeTemporaryPassword =
'/door-lock/temporary-password/offline/multiple-time/{doorLockUuid}';
static const String renamePassword =
'/door-lock/temporary-password/{doorLockUuid}/offline/{passwordId}';
//multiple-time offline
static const String deleteTemporaryPassword =
'/door-lock/temporary-password/online/{doorLockUuid}/{passwordId}';
} }

View File

@ -69,6 +69,24 @@ class DevicesAPI {
return response; return response;
} }
static Future<Map<String, dynamic>> renamePass({
required String name,
required String doorLockUuid,
required String passwordId}) async {
final response = await _httpService.put(
path: ApiEndpoints.renamePassword
.replaceAll('{doorLockUuid}', doorLockUuid)
.replaceAll('{passwordId}', passwordId),
body: {
"name": name
},
expectedResponseModel: (json) {
return json;
},
);
return response;
}
/// Get Device Functions /// Get Device Functions
static Future<FunctionModel> deviceFunctions(String deviceId) async { static Future<FunctionModel> deviceFunctions(String deviceId) async {
final response = await _httpService.get( final response = await _httpService.get(
@ -138,14 +156,9 @@ class DevicesAPI {
return response; return response;
} }
static Future getTemporaryPasswords(String deviceId, pageType) async { static Future getTemporaryPasswords(String deviceId, ) async {
final response = await _httpService.get( final response = await _httpService.get(
path: pageType == 'One-Time' path: ApiEndpoints.getTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
? ApiEndpoints.getOneTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId)
: pageType == 'Online Password'
? ApiEndpoints.getTemporaryPassword.replaceAll('{doorLockUuid}', deviceId)
: ApiEndpoints.getMultipleTimeTemporaryPassword
.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) { expectedResponseModel: (json) {
return json; return json;
@ -154,37 +167,48 @@ class DevicesAPI {
return response; 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) {
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) {
return json;
},
);
return response;
}
//create password
static Future createPassword({ static Future createPassword({
required String name, required String name,
required String password, required String password,
required String effectiveTime, required String effectiveTime,
required String invalidTime, required String invalidTime,
required String deviceId, required String deviceId,
required String pageType, List<Schedule>? scheduleList,}) async {
List<Schedule>? 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);
}
Map<String, dynamic> body = { Map<String, dynamic> body = {
"name": name, "name": name,
"password": password, "password": password,
"effectiveTime": effectiveTime, "effectiveTime": effectiveTime,
"invalidTime": invalidTime, "invalidTime": invalidTime,
}; };
if (pageType == 'Online Password' && scheduleList != null) { if (scheduleList != null) {
body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList(); body["scheduleList"] = scheduleList.map((schedule) => schedule.toJson()).toList();
} }
final response = await _httpService.post( final response = await _httpService.post(
path: endpointPath, path: ApiEndpoints.addTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
body: body, body: body,
showServerMessage: false, showServerMessage: false,
expectedResponseModel: (json) => json, expectedResponseModel: (json) => json,
@ -192,6 +216,40 @@ class DevicesAPI {
return response; return response;
} }
static Future generateOneTimePassword({deviceId}) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.addOneTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: false,
expectedResponseModel: (json) {
return json;
},
);
return response;
} catch (e) {
rethrow;
}
}
static Future generateMultiTimePassword({deviceId,effectiveTime,invalidTime}) async {
try {
final response = await _httpService.post(
path: ApiEndpoints.addMultipleTimeTemporaryPassword.replaceAll('{doorLockUuid}', deviceId),
showServerMessage: false,
body: {
"effectiveTime": effectiveTime,
"invalidTime": invalidTime
},
expectedResponseModel: (json) {
return json;
},
);
return response;
} catch (e) {
rethrow;
}
}
static Future<Map<String, dynamic>> deletePassword( static Future<Map<String, dynamic>> deletePassword(
{required String passwordId, required String deviceId}) async { {required String passwordId, required String deviceId}) async {
final response = await _httpService.delete( final response = await _httpService.delete(
@ -205,4 +263,5 @@ class DevicesAPI {
); );
return response; return response;
} }
} }