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()));
@ -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,10 +173,9 @@ 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,
)), )),
@ -279,121 +184,7 @@ class CreateTemporaryPassword extends StatelessWidget {
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,31 @@ 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) showServerMessage: false,
: pageType == 'Online Password' expectedResponseModel: (json) {
? ApiEndpoints.getTemporaryPassword.replaceAll('{doorLockUuid}', deviceId) return json;
: ApiEndpoints.getMultipleTimeTemporaryPassword },
.replaceAll('{doorLockUuid}', deviceId), );
return response;
}
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, showServerMessage: false,
expectedResponseModel: (json) { expectedResponseModel: (json) {
return json; return json;
@ -161,30 +196,19 @@ class DevicesAPI {
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;
} }
} }