mirror of
https://github.com/SyncrowIOT/syncrow-app.git
synced 2025-08-26 05:49:40 +00:00
Merge pull request #66 from SyncrowIOT/Implement_Change_Password_Flow
change_password
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
import 'dart:io';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
@ -55,7 +56,8 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
|
||||
Future fetchUserInfo() async {
|
||||
try {
|
||||
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
||||
var uuid =
|
||||
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
||||
user = await ProfileApi().fetchUserInfo(uuid);
|
||||
emit(HomeUserInfoLoaded(user!)); // Emit state after fetching user info
|
||||
} catch (e) {
|
||||
@ -123,7 +125,9 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
return;
|
||||
}
|
||||
|
||||
var userUuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? '';
|
||||
var userUuid =
|
||||
await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ??
|
||||
'';
|
||||
if (userUuid.isNotEmpty) {
|
||||
await OneSignal.login(userUuid);
|
||||
}
|
||||
@ -219,7 +223,8 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
//////////////////////////////////////// API ////////////////////////////////////////
|
||||
generateInvitation(SpaceModel unit) async {
|
||||
try {
|
||||
final invitationCode = await SpacesAPI.generateInvitationCode(unit.id, unit.community.uuid);
|
||||
final invitationCode =
|
||||
await SpacesAPI.generateInvitationCode(unit.id, unit.community.uuid);
|
||||
if (invitationCode.isNotEmpty) {
|
||||
Share.share('The invitation code is $invitationCode');
|
||||
CustomSnackBar.displaySnackBar(
|
||||
@ -235,7 +240,9 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
|
||||
Future<bool> joinAUnit(String code) async {
|
||||
try {
|
||||
var userUuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ?? '';
|
||||
var userUuid =
|
||||
await const FlutterSecureStorage().read(key: UserModel.userUuidKey) ??
|
||||
'';
|
||||
Map<String, String> body = {'inviteCode': code};
|
||||
|
||||
final success = await SpacesAPI.joinUnit(userUuid, body);
|
||||
@ -271,7 +278,8 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
fetchRoomsByUnitId(SpaceModel space) async {
|
||||
emitSafe(GetSpaceRoomsLoading());
|
||||
try {
|
||||
space.subspaces = await SpacesAPI.getSubSpaceBySpaceId(space.community.uuid, space.id);
|
||||
space.subspaces =
|
||||
await SpacesAPI.getSubSpaceBySpaceId(space.community.uuid, space.id);
|
||||
} catch (failure) {
|
||||
emitSafe(GetSpaceRoomsError(failure.toString()));
|
||||
return;
|
||||
@ -283,6 +291,29 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
}
|
||||
}
|
||||
|
||||
activationCode(activationCode) async {
|
||||
try {
|
||||
emitSafe(GetSpaceRoomsLoading());
|
||||
var uuid =
|
||||
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
||||
var res = await SpacesAPI.activationCodeSpace(
|
||||
activationCode: activationCode, userUuid: uuid);
|
||||
if (res['success'] == true) {
|
||||
fetchUserInfo();
|
||||
fetchUnitsByUserId();
|
||||
}
|
||||
emitSafe(GetSpacesSuccess(spaces!));
|
||||
return res['success'];
|
||||
} on DioException catch (e) {
|
||||
final errorMessage = e.response?.data['error']['message'];
|
||||
emitSafe(ActivationError(errMessage: errorMessage));
|
||||
return false;
|
||||
} catch (e) {
|
||||
emitSafe(ActivationError(errMessage: e.toString()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////// Nav ///////////////////////////////////////
|
||||
|
||||
static int pageIndex = 0;
|
||||
@ -353,7 +384,8 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
size: 32,
|
||||
),
|
||||
style: ButtonStyle(
|
||||
foregroundColor: WidgetStateProperty.all(ColorsManager.textPrimaryColor),
|
||||
foregroundColor:
|
||||
WidgetStateProperty.all(ColorsManager.textPrimaryColor),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(
|
||||
@ -374,7 +406,8 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
NavigationService.navigatorKey.currentContext!
|
||||
.read<SmartSceneSelectBloc>()
|
||||
.add(const SmartSceneClearEvent());
|
||||
BlocProvider.of<EffectPeriodBloc>(NavigationService.navigatorKey.currentState!.context)
|
||||
BlocProvider.of<EffectPeriodBloc>(
|
||||
NavigationService.navigatorKey.currentState!.context)
|
||||
.add(ResetEffectivePeriod());
|
||||
NavigationService.navigatorKey.currentContext!
|
||||
.read<CreateSceneBloc>()
|
||||
@ -447,7 +480,8 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
|
||||
void updateDevice(String deviceId) async {
|
||||
try {
|
||||
final response = await DevicesAPI.firmwareDevice(deviceId: deviceId, firmwareVersion: '0');
|
||||
final response = await DevicesAPI.firmwareDevice(
|
||||
deviceId: deviceId, firmwareVersion: '0');
|
||||
if (response['success'] ?? false) {
|
||||
CustomSnackBar.displaySnackBar('No updates available');
|
||||
}
|
||||
@ -455,7 +489,8 @@ class HomeCubit extends Cubit<HomeState> {
|
||||
}
|
||||
}
|
||||
|
||||
BottomNavigationBarItem defaultBottomNavBarItem({required String icon, required String label}) {
|
||||
BottomNavigationBarItem defaultBottomNavBarItem(
|
||||
{required String icon, required String label}) {
|
||||
return BottomNavigationBarItem(
|
||||
icon: SvgPicture.asset(icon),
|
||||
activeIcon: SvgPicture.asset(
|
||||
|
@ -32,6 +32,12 @@ class GetSpacesError extends HomeError {
|
||||
//get rooms
|
||||
class GetSpaceRoomsLoading extends HomeLoading {}
|
||||
|
||||
class ActivationError extends HomeLoading {
|
||||
final String errMessage;
|
||||
|
||||
ActivationError({this.errMessage = ''});
|
||||
}
|
||||
|
||||
class GetSpaceRoomsSuccess extends HomeSuccess {
|
||||
final List<SubSpaceModel> rooms;
|
||||
|
||||
@ -58,6 +64,7 @@ class RoomSelected extends HomeState {
|
||||
class RoomUnSelected extends HomeState {}
|
||||
|
||||
class NavChangePage extends HomeState {}
|
||||
|
||||
// Define new state classes
|
||||
class HomeUserInfoLoaded extends HomeState {
|
||||
final UserModel user;
|
||||
|
@ -204,6 +204,7 @@ class AuthCubit extends Cubit<AuthState> {
|
||||
const FlutterSecureStorage().write(
|
||||
key: UserModel.userUuidKey,
|
||||
value: Token.decodeToken(token.accessToken)['uuid'].toString());
|
||||
|
||||
user = UserModel.fromToken(token);
|
||||
emailController.clear();
|
||||
passwordController.clear();
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:syncrow_app/features/auth/model/token.dart';
|
||||
|
||||
@ -14,6 +13,7 @@ class UserModel {
|
||||
final bool? isEmailVerified;
|
||||
final String? regionName;
|
||||
final String? timeZone;
|
||||
final String? regionUuid;
|
||||
final bool? isAgreementAccepted;
|
||||
|
||||
UserModel({
|
||||
@ -24,10 +24,11 @@ class UserModel {
|
||||
required this.profilePicture,
|
||||
required this.phoneNumber,
|
||||
required this.isEmailVerified,
|
||||
required this.regionUuid,
|
||||
required this.isAgreementAccepted,
|
||||
required this.regionName, // Add this line
|
||||
required this.timeZone, // Add this line
|
||||
|
||||
required this.regionName,
|
||||
required this.timeZone,
|
||||
// required this.role,
|
||||
});
|
||||
|
||||
factory UserModel.fromJson(Map<String, dynamic> json) {
|
||||
@ -40,23 +41,24 @@ class UserModel {
|
||||
phoneNumber: json['phoneNumber'],
|
||||
isEmailVerified: json['isEmailVerified'],
|
||||
isAgreementAccepted: json['isAgreementAccepted'],
|
||||
regionName: json['region']?['regionName'], // Extract regionName
|
||||
timeZone: json['timeZone']?['timeZoneOffset'], // Extract regionName
|
||||
regionName: json['region']?['regionName'],
|
||||
timeZone: json['timeZone']?['timeZoneOffset'],
|
||||
regionUuid: json['region']?['uuid'],
|
||||
);
|
||||
}
|
||||
//uuid to json
|
||||
//from token
|
||||
factory UserModel.fromToken(Token token) {
|
||||
|
||||
factory UserModel.fromToken(Token token) {
|
||||
Map<String, dynamic> tempJson = Token.decodeToken(token.accessToken);
|
||||
return UserModel(
|
||||
uuid: tempJson['uuid'].toString(),
|
||||
email: tempJson['email'],
|
||||
lastName: tempJson['lastName'],
|
||||
firstName:tempJson['firstName'] ,
|
||||
firstName: tempJson['firstName'],
|
||||
profilePicture: UserModel.decodeBase64Image(tempJson['profilePicture']),
|
||||
phoneNumber: null,
|
||||
isEmailVerified: null,
|
||||
isAgreementAccepted: null,
|
||||
regionUuid: null,
|
||||
regionName: tempJson['region']?['regionName'],
|
||||
timeZone: tempJson['timezone']?['timeZoneOffset'],
|
||||
);
|
||||
@ -71,14 +73,18 @@ class UserModel {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': uuid,
|
||||
'uuid': uuid,
|
||||
'email': email,
|
||||
'lastName': lastName,
|
||||
'firstName': firstName,
|
||||
'photoUrl': profilePicture,
|
||||
'lastName': lastName,
|
||||
'profilePicture':
|
||||
profilePicture != null ? base64.encode(profilePicture!) : null,
|
||||
'phoneNumber': phoneNumber,
|
||||
'isEmailVerified': isEmailVerified,
|
||||
'regionUuid': regionUuid,
|
||||
'isAgreementAccepted': isAgreementAccepted,
|
||||
'regionName': regionName,
|
||||
'timeZone': timeZone,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import 'package:syncrow_app/utils/resource_manager/constants.dart';
|
||||
class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
final String acId;
|
||||
AcStatusModel deviceStatus = AcStatusModel(
|
||||
countdown1: 0,
|
||||
uuid: '',
|
||||
acSwitch: true,
|
||||
modeString: 'hot',
|
||||
@ -41,6 +42,12 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
on<IncreaseAllTemp>(_increaseAllTemp);
|
||||
on<DecreaseAllTemp>(_decreaseAllTemp);
|
||||
on<AcUpdated>(_onAcUpdated);
|
||||
on<OnClose>(_onClose);
|
||||
on<GetCounterEvent>(_getCounterValue);
|
||||
on<SetCounterValue>(_setCounterValue);
|
||||
on<TickTimer>(_onTickTimer);
|
||||
|
||||
// on<SetTimeOutValue>(_setTimeOutAlarm);
|
||||
}
|
||||
|
||||
void _fetchAcsStatus(AcsInitial event, Emitter<AcsState> emit) async {
|
||||
@ -61,7 +68,8 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
for (var status in response['status']) {
|
||||
statusModelList.add(StatusModel.fromJson(status));
|
||||
}
|
||||
deviceStatus = AcStatusModel.fromJson(response['productUuid'], statusModelList);
|
||||
deviceStatus =
|
||||
AcStatusModel.fromJson(response['productUuid'], statusModelList);
|
||||
emit(GetAcStatusState(acStatusModel: deviceStatus));
|
||||
Future.delayed(const Duration(milliseconds: 500));
|
||||
// _listenToChanges();
|
||||
@ -74,18 +82,22 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
|
||||
_listenToChanges() {
|
||||
try {
|
||||
DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$acId');
|
||||
DatabaseReference ref =
|
||||
FirebaseDatabase.instance.ref('device-status/$acId');
|
||||
Stream<DatabaseEvent> stream = ref.onValue;
|
||||
|
||||
stream.listen((DatabaseEvent event) {
|
||||
Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>;
|
||||
Map<dynamic, dynamic> usersMap =
|
||||
event.snapshot.value as Map<dynamic, dynamic>;
|
||||
List<StatusModel> statusList = [];
|
||||
|
||||
usersMap['status'].forEach((element) {
|
||||
statusList.add(StatusModel(code: element['code'], value: element['value']));
|
||||
statusList
|
||||
.add(StatusModel(code: element['code'], value: element['value']));
|
||||
});
|
||||
|
||||
deviceStatus = AcStatusModel.fromJson(usersMap['productUuid'], statusList);
|
||||
deviceStatus =
|
||||
AcStatusModel.fromJson(usersMap['productUuid'], statusList);
|
||||
add(AcUpdated());
|
||||
});
|
||||
} catch (_) {}
|
||||
@ -102,12 +114,14 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
HomeCubit.getInstance().selectedSpace?.id ?? '', 'AC');
|
||||
|
||||
for (int i = 0; i < devicesList.length; i++) {
|
||||
var response = await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? '');
|
||||
var response =
|
||||
await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? '');
|
||||
List<StatusModel> statusModelList = [];
|
||||
for (var status in response['status']) {
|
||||
statusModelList.add(StatusModel.fromJson(status));
|
||||
}
|
||||
deviceStatusList.add(AcStatusModel.fromJson(response['productUuid'], statusModelList));
|
||||
deviceStatusList.add(
|
||||
AcStatusModel.fromJson(response['productUuid'], statusModelList));
|
||||
}
|
||||
_setAllAcsTempsAndSwitches();
|
||||
}
|
||||
@ -129,7 +143,8 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
emit(AcModifyingState(acStatusModel: deviceStatus));
|
||||
}
|
||||
|
||||
await _runDeBouncerForOneDevice(deviceId: event.deviceId, code: 'switch', value: acSwitchValue);
|
||||
await _runDeBouncerForOneDevice(
|
||||
deviceId: event.deviceId, code: 'switch', value: acSwitchValue);
|
||||
}
|
||||
|
||||
void _changeAllAcSwitch(ChangeAllSwitch event, Emitter<AcsState> emit) async {
|
||||
@ -190,7 +205,8 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
deviceStatus.childLock = lockValue;
|
||||
emit(AcModifyingState(acStatusModel: deviceStatus));
|
||||
|
||||
await _runDeBouncerForOneDevice(deviceId: acId, code: 'child_lock', value: lockValue);
|
||||
await _runDeBouncerForOneDevice(
|
||||
deviceId: acId, code: 'child_lock', value: lockValue);
|
||||
}
|
||||
|
||||
void _increaseCoolTo(IncreaseCoolToTemp event, Emitter<AcsState> emit) async {
|
||||
@ -218,7 +234,8 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
emit(AcModifyingState(acStatusModel: deviceStatus));
|
||||
}
|
||||
|
||||
await _runDeBouncerForOneDevice(deviceId: event.deviceId, code: 'temp_set', value: value);
|
||||
await _runDeBouncerForOneDevice(
|
||||
deviceId: event.deviceId, code: 'temp_set', value: value);
|
||||
}
|
||||
|
||||
void _decreaseCoolTo(DecreaseCoolToTemp event, Emitter<AcsState> emit) async {
|
||||
@ -246,7 +263,8 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
emit(AcModifyingState(acStatusModel: deviceStatus));
|
||||
}
|
||||
|
||||
await _runDeBouncerForOneDevice(deviceId: event.deviceId, code: 'temp_set', value: value);
|
||||
await _runDeBouncerForOneDevice(
|
||||
deviceId: event.deviceId, code: 'temp_set', value: value);
|
||||
}
|
||||
|
||||
void _changeAcMode(ChangeAcMode event, Emitter<AcsState> emit) async {
|
||||
@ -268,7 +286,9 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
}
|
||||
|
||||
await _runDeBouncerForOneDevice(
|
||||
deviceId: event.deviceId, code: 'mode', value: getACModeString(tempMode));
|
||||
deviceId: event.deviceId,
|
||||
code: 'mode',
|
||||
value: getACModeString(tempMode));
|
||||
}
|
||||
|
||||
void _changeFanSpeed(ChangeFanSpeed event, Emitter<AcsState> emit) async {
|
||||
@ -281,19 +301,23 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
for (AcStatusModel ac in deviceStatusList) {
|
||||
if (ac.uuid == event.productId) {
|
||||
ac.fanSpeedsString = getNextFanSpeedKey(fanSpeed);
|
||||
ac.acFanSpeed = AcStatusModel.getFanSpeed(getNextFanSpeedKey(fanSpeed));
|
||||
ac.acFanSpeed =
|
||||
AcStatusModel.getFanSpeed(getNextFanSpeedKey(fanSpeed));
|
||||
}
|
||||
}
|
||||
_emitAcsStatus(emit);
|
||||
} else {
|
||||
emit(AcChangeLoading(acStatusModel: deviceStatus));
|
||||
deviceStatus.fanSpeedsString = getNextFanSpeedKey(fanSpeed);
|
||||
deviceStatus.acFanSpeed = AcStatusModel.getFanSpeed(getNextFanSpeedKey(fanSpeed));
|
||||
deviceStatus.acFanSpeed =
|
||||
AcStatusModel.getFanSpeed(getNextFanSpeedKey(fanSpeed));
|
||||
emit(AcModifyingState(acStatusModel: deviceStatus));
|
||||
}
|
||||
|
||||
await _runDeBouncerForOneDevice(
|
||||
deviceId: event.deviceId, code: 'level', value: getNextFanSpeedKey(fanSpeed));
|
||||
deviceId: event.deviceId,
|
||||
code: 'level',
|
||||
value: getNextFanSpeedKey(fanSpeed));
|
||||
}
|
||||
|
||||
String getACModeString(TempModes value) {
|
||||
@ -338,7 +362,8 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
for (int i = 0; i < deviceStatusList.length; i++) {
|
||||
try {
|
||||
await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(deviceId: devicesList[i].uuid, code: code, value: value),
|
||||
DeviceControlModel(
|
||||
deviceId: devicesList[i].uuid, code: code, value: value),
|
||||
devicesList[i].uuid ?? '');
|
||||
} catch (_) {
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
@ -360,7 +385,10 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
_timer = Timer(const Duration(seconds: 1), () async {
|
||||
try {
|
||||
final response = await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(deviceId: allAcsPage ? deviceId : acId, code: code, value: value),
|
||||
DeviceControlModel(
|
||||
deviceId: allAcsPage ? deviceId : acId,
|
||||
code: code,
|
||||
value: value),
|
||||
allAcsPage ? deviceId : acId);
|
||||
|
||||
if (!response['success']) {
|
||||
@ -377,7 +405,8 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
if (value >= 20 && value <= 30) {
|
||||
return true;
|
||||
} else {
|
||||
emit(const AcsFailedState(errorMessage: 'The temperature must be between 20 and 30'));
|
||||
emit(const AcsFailedState(
|
||||
errorMessage: 'The temperature must be between 20 and 30'));
|
||||
emit(GetAllAcsStatusState(
|
||||
allAcsStatues: deviceStatusList,
|
||||
allAcs: devicesList,
|
||||
@ -396,4 +425,74 @@ class ACsBloc extends Bloc<AcsEvent, AcsState> {
|
||||
allTempSame: allTempSame,
|
||||
temp: globalTemp));
|
||||
}
|
||||
|
||||
void _setCounterValue(SetCounterValue event, Emitter<AcsState> emit) async {
|
||||
emit(AcsLoadingState());
|
||||
int seconds = 0;
|
||||
try {
|
||||
seconds = event.seconds;
|
||||
final response = await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(
|
||||
deviceId: acId, code: 'countdown_time', value: event.duration),
|
||||
acId);
|
||||
|
||||
if (response['success'] ?? false) {
|
||||
deviceStatus.countdown1 = seconds;
|
||||
} else {
|
||||
emit(const AcsFailedState(errorMessage: 'Something went wrong'));
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
emit(AcsFailedState(errorMessage: e.toString()));
|
||||
return;
|
||||
}
|
||||
if (event.duration > 0) {
|
||||
_onStartTimer(seconds);
|
||||
} else {
|
||||
_timer?.cancel();
|
||||
emit(TimerRunComplete());
|
||||
}
|
||||
}
|
||||
|
||||
void _getCounterValue(GetCounterEvent event, Emitter<AcsState> emit) async {
|
||||
try {
|
||||
emit(AcsLoadingState());
|
||||
var response = await DevicesAPI.getDeviceStatus(acId);
|
||||
List<StatusModel> statusModelList = [];
|
||||
for (var status in response['status']) {
|
||||
statusModelList.add(StatusModel.fromJson(status));
|
||||
}
|
||||
deviceStatus =
|
||||
AcStatusModel.fromJson(response['productUuid'], statusModelList);
|
||||
|
||||
if (event.deviceCode == 'countdown_time') {
|
||||
deviceStatus.countdown1 > 0
|
||||
? _onStartTimer(deviceStatus.countdown1)
|
||||
: emit(UpdateTimerState(seconds: deviceStatus.countdown1));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(AcsFailedState(errorMessage: e.toString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void _onStartTimer(int seconds) {
|
||||
_timer?.cancel();
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
add(TickTimer(remainingTime: seconds - timer.tick));
|
||||
});
|
||||
}
|
||||
|
||||
void _onTickTimer(TickTimer event, Emitter<AcsState> emit) {
|
||||
if (event.remainingTime > 0) {
|
||||
emit(TimerRunInProgress(event.remainingTime));
|
||||
} else {
|
||||
_timer?.cancel();
|
||||
emit(TimerRunComplete());
|
||||
}
|
||||
}
|
||||
|
||||
void _onClose(OnClose event, Emitter<AcsState> emit) {
|
||||
_timer?.cancel(); // Cancel the timer
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ class AcSwitch extends AcsEvent {
|
||||
final bool acSwitch;
|
||||
final String deviceId;
|
||||
final String productId;
|
||||
const AcSwitch({required this.acSwitch, this.deviceId = '', this.productId = ''});
|
||||
const AcSwitch(
|
||||
{required this.acSwitch, this.deviceId = '', this.productId = ''});
|
||||
|
||||
@override
|
||||
List<Object> get props => [acSwitch, deviceId, productId];
|
||||
@ -35,7 +36,8 @@ class IncreaseCoolToTemp extends AcsEvent {
|
||||
final double value;
|
||||
final String deviceId;
|
||||
final String productId;
|
||||
const IncreaseCoolToTemp({required this.value, this.deviceId = '', this.productId = ''});
|
||||
const IncreaseCoolToTemp(
|
||||
{required this.value, this.deviceId = '', this.productId = ''});
|
||||
|
||||
@override
|
||||
List<Object> get props => [value, deviceId];
|
||||
@ -46,7 +48,8 @@ class DecreaseCoolToTemp extends AcsEvent {
|
||||
final String deviceId;
|
||||
final String productId;
|
||||
|
||||
const DecreaseCoolToTemp({required this.value, this.deviceId = '', this.productId = ''});
|
||||
const DecreaseCoolToTemp(
|
||||
{required this.value, this.deviceId = '', this.productId = ''});
|
||||
|
||||
@override
|
||||
List<Object> get props => [value, deviceId];
|
||||
@ -56,7 +59,8 @@ class ChangeAcMode extends AcsEvent {
|
||||
final TempModes tempModes;
|
||||
final String deviceId;
|
||||
final String productId;
|
||||
const ChangeAcMode({required this.tempModes, this.deviceId = '', this.productId = ''});
|
||||
const ChangeAcMode(
|
||||
{required this.tempModes, this.deviceId = '', this.productId = ''});
|
||||
|
||||
@override
|
||||
List<Object> get props => [tempModes, deviceId, productId];
|
||||
@ -67,7 +71,8 @@ class ChangeFanSpeed extends AcsEvent {
|
||||
final String deviceId;
|
||||
final String productId;
|
||||
|
||||
const ChangeFanSpeed({required this.fanSpeeds, this.deviceId = '', this.productId = ''});
|
||||
const ChangeFanSpeed(
|
||||
{required this.fanSpeeds, this.deviceId = '', this.productId = ''});
|
||||
|
||||
@override
|
||||
List<Object> get props => [fanSpeeds, deviceId, productId];
|
||||
@ -104,3 +109,52 @@ class DecreaseAllTemp extends AcsEvent {
|
||||
@override
|
||||
List<Object> get props => [value];
|
||||
}
|
||||
|
||||
class SetCounterValue extends AcsEvent {
|
||||
final int duration;
|
||||
final String deviceCode;
|
||||
final int seconds;
|
||||
const SetCounterValue(
|
||||
{required this.duration,
|
||||
required this.deviceCode,
|
||||
required this.seconds});
|
||||
@override
|
||||
List<Object> get props => [duration, deviceCode, seconds];
|
||||
}
|
||||
|
||||
class SetTimeOutValue extends AcsEvent {
|
||||
final Duration duration;
|
||||
final String deviceCode;
|
||||
const SetTimeOutValue({required this.duration, required this.deviceCode});
|
||||
@override
|
||||
List<Object> get props => [duration, deviceCode];
|
||||
}
|
||||
|
||||
class StartTimer extends AcsEvent {
|
||||
final int duration;
|
||||
|
||||
const StartTimer(this.duration);
|
||||
|
||||
@override
|
||||
List<Object> get props => [duration];
|
||||
}
|
||||
|
||||
class TickTimer extends AcsEvent {
|
||||
final int remainingTime;
|
||||
|
||||
const TickTimer({required this.remainingTime});
|
||||
|
||||
@override
|
||||
List<Object> get props => [remainingTime];
|
||||
}
|
||||
|
||||
class StopTimer extends AcsEvent {}
|
||||
|
||||
class GetCounterEvent extends AcsEvent {
|
||||
final String deviceCode;
|
||||
const GetCounterEvent({required this.deviceCode});
|
||||
@override
|
||||
List<Object> get props => [deviceCode];
|
||||
}
|
||||
|
||||
class OnClose extends AcsEvent {}
|
||||
|
@ -63,3 +63,22 @@ class AcsFailedState extends AcsState {
|
||||
@override
|
||||
List<Object> get props => [errorMessage];
|
||||
}
|
||||
|
||||
class UpdateTimerState extends AcsState {
|
||||
final int seconds;
|
||||
const UpdateTimerState({required this.seconds});
|
||||
|
||||
@override
|
||||
List<Object> get props => [seconds];
|
||||
}
|
||||
|
||||
class TimerRunInProgress extends AcsState {
|
||||
final int remainingTime;
|
||||
|
||||
const TimerRunInProgress(this.remainingTime);
|
||||
|
||||
@override
|
||||
List<Object> get props => [remainingTime];
|
||||
}
|
||||
|
||||
class TimerRunComplete extends AcsState {}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:firebase_database/firebase_database.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/ceiling_bloc/ceiling_sensor_event.dart';
|
||||
@ -5,6 +6,7 @@ import 'package:syncrow_app/features/devices/bloc/ceiling_bloc/ceiling_sensor_st
|
||||
import 'package:syncrow_app/features/devices/model/ceiling_sensor_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_control_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_report_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/status_model.dart';
|
||||
import 'package:syncrow_app/services/api/devices_api.dart';
|
||||
|
||||
@ -17,9 +19,11 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
|
||||
on<InitialEvent>(_fetchCeilingSensorStatus);
|
||||
on<ChangeValueEvent>(_changeValue);
|
||||
on<CeilingSensorUpdated>(_onCeilingSensorUpdated);
|
||||
on<ReportLogsInitial>(fetchLogsForLastMonth);
|
||||
}
|
||||
|
||||
void _fetchCeilingSensorStatus(InitialEvent event, Emitter<CeilingSensorState> emit) async {
|
||||
void _fetchCeilingSensorStatus(
|
||||
InitialEvent event, Emitter<CeilingSensorState> emit) async {
|
||||
emit(LoadingInitialState());
|
||||
try {
|
||||
var response = await DevicesAPI.getDeviceStatus(deviceId);
|
||||
@ -38,15 +42,18 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
|
||||
|
||||
_listenToChanges() {
|
||||
try {
|
||||
DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$deviceId');
|
||||
DatabaseReference ref =
|
||||
FirebaseDatabase.instance.ref('device-status/$deviceId');
|
||||
Stream<DatabaseEvent> stream = ref.onValue;
|
||||
|
||||
stream.listen((DatabaseEvent event) {
|
||||
Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>;
|
||||
Map<dynamic, dynamic> usersMap =
|
||||
event.snapshot.value as Map<dynamic, dynamic>;
|
||||
List<StatusModel> statusList = [];
|
||||
|
||||
usersMap['status'].forEach((element) {
|
||||
statusList.add(StatusModel(code: element['code'], value: element['value']));
|
||||
statusList
|
||||
.add(StatusModel(code: element['code'], value: element['value']));
|
||||
});
|
||||
|
||||
deviceStatus = CeilingSensorModel.fromJson(statusList);
|
||||
@ -55,20 +62,62 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
_onCeilingSensorUpdated(CeilingSensorUpdated event, Emitter<CeilingSensorState> emit) {
|
||||
_onCeilingSensorUpdated(
|
||||
CeilingSensorUpdated event, Emitter<CeilingSensorState> emit) {
|
||||
emit(UpdateState(ceilingSensorModel: deviceStatus));
|
||||
}
|
||||
|
||||
void _changeValue(ChangeValueEvent event, Emitter<CeilingSensorState> emit) async {
|
||||
void _changeValue(
|
||||
ChangeValueEvent event, Emitter<CeilingSensorState> emit) async {
|
||||
emit(LoadingNewSate(ceilingSensorModel: deviceStatus));
|
||||
try {
|
||||
final response = await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(deviceId: deviceId, code: event.code, value: event.value), deviceId);
|
||||
|
||||
DeviceControlModel(
|
||||
deviceId: deviceId, code: event.code, value: event.value),
|
||||
deviceId);
|
||||
if (response['success'] ?? false) {
|
||||
deviceStatus.sensitivity = event.value;
|
||||
}
|
||||
if (event.type == 'Sensitivity') {
|
||||
deviceStatus.sensitivity = event.value;
|
||||
} else if (event.type == 'Maximum Distance') {
|
||||
deviceStatus.movingMaxDis = event.value;
|
||||
} else if (event.type == 'Nobody Time') {
|
||||
deviceStatus.nobodyTime = event.value;
|
||||
} else if (event.type == 'Space Type') {
|
||||
deviceStatus.spaceType = SpaceTypes.values.firstWhere(
|
||||
(e) => e.name == event.value,
|
||||
orElse: () => SpaceTypes.none,
|
||||
);
|
||||
}
|
||||
} //scene
|
||||
emit(UpdateState(ceilingSensorModel: deviceStatus));
|
||||
} catch (_) {}
|
||||
emit(UpdateState(ceilingSensorModel: deviceStatus));
|
||||
}
|
||||
|
||||
DeviceReport recordGroups =
|
||||
DeviceReport(startTime: '0', endTime: '0', data: []);
|
||||
|
||||
Future<void> fetchLogsForLastMonth(
|
||||
ReportLogsInitial event, Emitter<CeilingSensorState> emit) async {
|
||||
DateTime now = DateTime.now();
|
||||
|
||||
DateTime lastMonth = DateTime(now.year, now.month - 1, now.day);
|
||||
int startTime = lastMonth.millisecondsSinceEpoch;
|
||||
int endTime = now.millisecondsSinceEpoch;
|
||||
try {
|
||||
emit(LoadingInitialState());
|
||||
var response = await DevicesAPI.getReportLogs(
|
||||
startTime: startTime.toString(),
|
||||
endTime: endTime.toString(),
|
||||
deviceUuid: deviceId,
|
||||
code: 'presence_state',
|
||||
);
|
||||
recordGroups = response;
|
||||
// print('---${recordGroups.data!.first.eventTime}');
|
||||
emit(FitchData());
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage = errorData['message'];
|
||||
emit(FailedState(error: errorMessage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,10 +14,16 @@ class InitialEvent extends CeilingSensorEvent {}
|
||||
class CeilingSensorUpdated extends CeilingSensorEvent {}
|
||||
|
||||
class ChangeValueEvent extends CeilingSensorEvent {
|
||||
final int value;
|
||||
final dynamic value;
|
||||
final String code;
|
||||
const ChangeValueEvent({required this.value, required this.code});
|
||||
final String type;
|
||||
|
||||
const ChangeValueEvent({required this.value, required this.code,required this.type});
|
||||
|
||||
@override
|
||||
List<Object> get props => [value, code];
|
||||
List<Object> get props => [value, code,type];
|
||||
}
|
||||
|
||||
class ReportLogsInitial extends CeilingSensorEvent {
|
||||
const ReportLogsInitial();
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ class CeilingSensorState extends Equatable {
|
||||
|
||||
class InitialState extends CeilingSensorState {}
|
||||
|
||||
class FitchData extends CeilingSensorState {}
|
||||
|
||||
class LoadingInitialState extends CeilingSensorState {}
|
||||
|
||||
class UpdateState extends CeilingSensorState {
|
||||
|
@ -65,7 +65,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
batteryPercentage: 0,
|
||||
);
|
||||
|
||||
void _fetchStatus(GarageDoorInitial event, Emitter<GarageDoorSensorState> emit) async {
|
||||
void _fetchStatus(
|
||||
GarageDoorInitial event, Emitter<GarageDoorSensorState> emit) async {
|
||||
emit(GarageDoorLoadingState());
|
||||
try {
|
||||
var response = await DevicesAPI.getDeviceStatus(GDId);
|
||||
@ -113,8 +114,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _toggleClosingReminder(
|
||||
ToggleClosingReminderEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
void _toggleClosingReminder(ToggleClosingReminderEvent event,
|
||||
Emitter<GarageDoorSensorState> emit) async {
|
||||
emit(LoadingNewSate(doorSensor: deviceStatus));
|
||||
try {
|
||||
closingReminder = event.isClosingReminderEnabled;
|
||||
@ -132,7 +133,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _toggleDoorAlarm(ToggleDoorAlarmEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
void _toggleDoorAlarm(
|
||||
ToggleDoorAlarmEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
emit(LoadingNewSate(doorSensor: deviceStatus));
|
||||
try {
|
||||
doorAlarm = event.isDoorAlarmEnabled;
|
||||
@ -150,7 +152,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
}
|
||||
}
|
||||
|
||||
DeviceReport recordGroups = DeviceReport(startTime: '0', endTime: '0', data: []);
|
||||
DeviceReport recordGroups =
|
||||
DeviceReport(startTime: '0', endTime: '0', data: []);
|
||||
|
||||
Future<void> fetchLogsForLastMonth(
|
||||
ReportLogsInitial event, Emitter<GarageDoorSensorState> emit) async {
|
||||
@ -179,14 +182,16 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
|
||||
_listenToChanges() {
|
||||
try {
|
||||
DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$GDId');
|
||||
DatabaseReference ref =
|
||||
FirebaseDatabase.instance.ref('device-status/$GDId');
|
||||
Stream<DatabaseEvent> stream = ref.onValue;
|
||||
|
||||
stream.listen((DatabaseEvent event) async {
|
||||
if (_timer != null) {
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
}
|
||||
Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>;
|
||||
Map<dynamic, dynamic> usersMap =
|
||||
event.snapshot.value as Map<dynamic, dynamic>;
|
||||
List<StatusModel> statusList = [];
|
||||
usersMap['status'].forEach((element) {
|
||||
statusList.add(StatusModel(code: element['code'], value: true));
|
||||
@ -261,7 +266,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
deviceId: GDId,
|
||||
);
|
||||
List<dynamic> jsonData = response;
|
||||
listSchedule = jsonData.map((item) => ScheduleModel.fromJson(item)).toList();
|
||||
listSchedule =
|
||||
jsonData.map((item) => ScheduleModel.fromJson(item)).toList();
|
||||
emit(UpdateState(garageSensor: deviceStatus));
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
@ -272,12 +278,13 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
|
||||
int? getTimeStampWithoutSeconds(DateTime? dateTime) {
|
||||
if (dateTime == null) return null;
|
||||
DateTime dateTimeWithoutSeconds =
|
||||
DateTime(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute);
|
||||
DateTime dateTimeWithoutSeconds = DateTime(dateTime.year, dateTime.month,
|
||||
dateTime.day, dateTime.hour, dateTime.minute);
|
||||
return dateTimeWithoutSeconds.millisecondsSinceEpoch ~/ 1000;
|
||||
}
|
||||
|
||||
Future toggleChange(ToggleScheduleEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
Future toggleChange(
|
||||
ToggleScheduleEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
try {
|
||||
emit(GarageDoorLoadingState());
|
||||
final response = await DevicesAPI.changeSchedule(
|
||||
@ -295,7 +302,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future deleteSchedule(DeleteScheduleEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
Future deleteSchedule(
|
||||
DeleteScheduleEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
try {
|
||||
emit(GarageDoorLoadingState());
|
||||
final response = await DevicesAPI.deleteSchedule(
|
||||
@ -314,13 +322,15 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
}
|
||||
}
|
||||
|
||||
void toggleSelectedIndex(ToggleSelectedEvent event, Emitter<GarageDoorSensorState> emit) {
|
||||
void toggleSelectedIndex(
|
||||
ToggleSelectedEvent event, Emitter<GarageDoorSensorState> emit) {
|
||||
emit(GarageDoorLoadingState());
|
||||
selectedTabIndex = event.index;
|
||||
emit(ChangeSlidingSegmentState(value: selectedTabIndex));
|
||||
}
|
||||
|
||||
void toggleCreateSchedule(ToggleCreateScheduleEvent event, Emitter<GarageDoorSensorState> emit) {
|
||||
void toggleCreateSchedule(
|
||||
ToggleCreateScheduleEvent event, Emitter<GarageDoorSensorState> emit) {
|
||||
emit(GarageDoorLoadingState());
|
||||
createSchedule = !createSchedule;
|
||||
selectedDays.clear();
|
||||
@ -337,13 +347,16 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
|
||||
int secondSelected = 0;
|
||||
bool toggleDoor = false;
|
||||
Future<void> selectSeconds(SelectSecondsEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
Future<void> selectSeconds(
|
||||
SelectSecondsEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
try {
|
||||
emit(GarageDoorLoadingState());
|
||||
secondSelected = event.seconds;
|
||||
|
||||
await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(deviceId: GDId, code: 'tr_timecon', value: secondSelected), GDId);
|
||||
DeviceControlModel(
|
||||
deviceId: GDId, code: 'tr_timecon', value: secondSelected),
|
||||
GDId);
|
||||
emit(UpdateState(garageSensor: deviceStatus));
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
@ -352,12 +365,15 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
}
|
||||
}
|
||||
|
||||
openCloseGarageDoor(ToggleDoorEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
openCloseGarageDoor(
|
||||
ToggleDoorEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
emit(GarageDoorLoadingState());
|
||||
try {
|
||||
toggleDoor = !event.toggle;
|
||||
await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(deviceId: GDId, code: 'switch_1', value: toggleDoor), GDId);
|
||||
DeviceControlModel(
|
||||
deviceId: GDId, code: 'switch_1', value: toggleDoor),
|
||||
GDId);
|
||||
add(const GarageDoorInitial());
|
||||
emit(UpdateState(garageSensor: deviceStatus));
|
||||
} on DioException catch (e) {
|
||||
@ -367,13 +383,16 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _setCounterValue(SetCounterValue event, Emitter<GarageDoorSensorState> emit) async {
|
||||
void _setCounterValue(
|
||||
SetCounterValue event, Emitter<GarageDoorSensorState> emit) async {
|
||||
emit(LoadingNewSate(doorSensor: deviceStatus));
|
||||
int seconds = 0;
|
||||
try {
|
||||
seconds = event.duration.inSeconds;
|
||||
final response = await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(deviceId: GDId, code: 'countdown_1', value: seconds), GDId);
|
||||
DeviceControlModel(
|
||||
deviceId: GDId, code: 'countdown_1', value: seconds),
|
||||
GDId);
|
||||
|
||||
if (response['success'] ?? false) {
|
||||
deviceStatus.countdown1 = seconds;
|
||||
@ -393,7 +412,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _getCounterValue(GetCounterEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
void _getCounterValue(
|
||||
GetCounterEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
emit(LoadingInitialState());
|
||||
try {
|
||||
var response = await DevicesAPI.getDeviceStatus(GDId);
|
||||
@ -434,7 +454,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
List<GroupGarageModel> groupList = [];
|
||||
bool allSwitchesOn = true;
|
||||
|
||||
void _fetchWizardStatus(InitialWizardEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
void _fetchWizardStatus(
|
||||
InitialWizardEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
emit(LoadingInitialState());
|
||||
try {
|
||||
devicesList = [];
|
||||
@ -444,7 +465,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
HomeCubit.getInstance().selectedSpace?.id ?? '', 'GD');
|
||||
|
||||
for (int i = 0; i < devicesList.length; i++) {
|
||||
var response = await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? '');
|
||||
var response =
|
||||
await DevicesAPI.getDeviceStatus(devicesList[i].uuid ?? '');
|
||||
List<StatusModel> statusModelList = [];
|
||||
for (var status in response['status']) {
|
||||
statusModelList.add(StatusModel.fromJson(status));
|
||||
@ -473,7 +495,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _groupAllOn(GroupAllOnEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
void _groupAllOn(
|
||||
GroupAllOnEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
emit(LoadingNewSate(doorSensor: deviceStatus));
|
||||
try {
|
||||
// Set all switches (firstSwitch and secondSwitch) based on the event value (on/off)
|
||||
@ -485,7 +508,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
emit(UpdateGroupState(garageList: groupList, allSwitches: true));
|
||||
|
||||
// Get a list of all device IDs
|
||||
List<String> allDeviceIds = groupList.map((device) => device.deviceId).toList();
|
||||
List<String> allDeviceIds =
|
||||
groupList.map((device) => device.deviceId).toList();
|
||||
|
||||
// First call for switch_1
|
||||
final response = await DevicesAPI.deviceBatchController(
|
||||
@ -505,7 +529,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _groupAllOff(GroupAllOffEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
void _groupAllOff(
|
||||
GroupAllOffEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
emit(LoadingNewSate(doorSensor: deviceStatus));
|
||||
try {
|
||||
// Set all switches (firstSwitch and secondSwitch) based on the event value (on/off)
|
||||
@ -517,7 +542,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
emit(UpdateGroupState(garageList: groupList, allSwitches: false));
|
||||
|
||||
// Get a list of all device IDs
|
||||
List<String> allDeviceIds = groupList.map((device) => device.deviceId).toList();
|
||||
List<String> allDeviceIds =
|
||||
groupList.map((device) => device.deviceId).toList();
|
||||
|
||||
// First call for switch_1
|
||||
final response = await DevicesAPI.deviceBatchController(
|
||||
@ -538,8 +564,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _changeFirstWizardSwitch(
|
||||
ChangeFirstWizardSwitchStatusEvent event, Emitter<GarageDoorSensorState> emit) async {
|
||||
void _changeFirstWizardSwitch(ChangeFirstWizardSwitchStatusEvent event,
|
||||
Emitter<GarageDoorSensorState> emit) async {
|
||||
emit(LoadingNewSate(doorSensor: deviceStatus));
|
||||
try {
|
||||
bool allSwitchesValue = true;
|
||||
@ -552,7 +578,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
}
|
||||
});
|
||||
|
||||
emit(UpdateGroupState(garageList: groupList, allSwitches: allSwitchesValue));
|
||||
emit(UpdateGroupState(
|
||||
garageList: groupList, allSwitches: allSwitchesValue));
|
||||
|
||||
final response = await DevicesAPI.deviceBatchController(
|
||||
code: 'switch_1',
|
||||
@ -568,13 +595,16 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorSensorState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _setTimeOutAlarm(SetTimeOutValue event, Emitter<GarageDoorSensorState> emit) async {
|
||||
void _setTimeOutAlarm(
|
||||
SetTimeOutValue event, Emitter<GarageDoorSensorState> emit) async {
|
||||
emit(LoadingNewSate(doorSensor: deviceStatus));
|
||||
int seconds = 0;
|
||||
try {
|
||||
seconds = event.duration.inSeconds;
|
||||
final response = await DevicesAPI.controlDevice(
|
||||
DeviceControlModel(deviceId: GDId, code: 'countdown_alarm', value: seconds), GDId);
|
||||
DeviceControlModel(
|
||||
deviceId: GDId, code: 'countdown_alarm', value: seconds),
|
||||
GDId);
|
||||
|
||||
if (response['success'] ?? false) {
|
||||
deviceStatus.countdownAlarm = seconds;
|
||||
|
@ -11,12 +11,14 @@ class AcStatusModel {
|
||||
bool childLock;
|
||||
late TempModes acMode;
|
||||
late FanSpeeds acFanSpeed;
|
||||
int countdown1;
|
||||
|
||||
AcStatusModel(
|
||||
{required this.uuid,
|
||||
required this.acSwitch,
|
||||
required this.modeString,
|
||||
required this.tempSet,
|
||||
required this.countdown1,
|
||||
required this.currentTemp,
|
||||
required this.fanSpeedsString,
|
||||
required this.childLock}) {
|
||||
@ -30,6 +32,7 @@ class AcStatusModel {
|
||||
late int _tempSet;
|
||||
late int _currentTemp;
|
||||
late String _fanSpeeds;
|
||||
late int _countdown1;
|
||||
late bool _childLock;
|
||||
for (int i = 0; i < jsonList.length; i++) {
|
||||
if (jsonList[i].code == 'switch') {
|
||||
@ -44,6 +47,8 @@ class AcStatusModel {
|
||||
_fanSpeeds = jsonList[i].value ?? 210;
|
||||
} else if (jsonList[i].code == 'child_lock') {
|
||||
_childLock = jsonList[i].value ?? false;
|
||||
} else if (jsonList[i].code == 'countdown_time') {
|
||||
_countdown1 = jsonList[i].value ?? 0;
|
||||
}
|
||||
}
|
||||
return AcStatusModel(
|
||||
@ -53,6 +58,7 @@ class AcStatusModel {
|
||||
tempSet: _tempSet,
|
||||
currentTemp: _currentTemp,
|
||||
fanSpeedsString: _fanSpeeds,
|
||||
countdown1: _countdown1,
|
||||
childLock: _childLock);
|
||||
}
|
||||
|
||||
|
@ -7,13 +7,19 @@ class CeilingSensorModel {
|
||||
int presenceRange;
|
||||
int sportsPara;
|
||||
String bodyMovement;
|
||||
String nobodyTime;
|
||||
int movingMaxDis;
|
||||
SpaceTypes spaceType;
|
||||
|
||||
CeilingSensorModel(
|
||||
{required this.presenceState,
|
||||
required this.sensitivity,
|
||||
required this.nobodyTime,
|
||||
required this.spaceType,
|
||||
required this.checkingResult,
|
||||
required this.presenceRange,
|
||||
required this.sportsPara,
|
||||
required this.movingMaxDis,
|
||||
required this.bodyMovement});
|
||||
|
||||
factory CeilingSensorModel.fromJson(List<StatusModel> jsonList) {
|
||||
@ -22,8 +28,10 @@ class CeilingSensorModel {
|
||||
late String _checkingResult;
|
||||
int _presenceRange = 1;
|
||||
int _sportsPara = 1;
|
||||
int _moving_max_dis = 0;
|
||||
String _bodyMovement = 'none';
|
||||
|
||||
String _nobody_time = 'none';
|
||||
SpaceTypes _spaceType = SpaceTypes.none;
|
||||
for (int i = 0; i < jsonList.length; i++) {
|
||||
if (jsonList[i].code == 'presence_state') {
|
||||
_presenceState = jsonList[i].value ?? 'none';
|
||||
@ -37,14 +45,46 @@ class CeilingSensorModel {
|
||||
_sportsPara = jsonList[i].value ?? 0;
|
||||
} else if (jsonList[i].code == 'body_movement') {
|
||||
_bodyMovement = jsonList[i].value ?? '';
|
||||
}
|
||||
} else if (jsonList[i].code == 'nobody_time') {
|
||||
_nobody_time = jsonList[i].value ?? 'none';
|
||||
} else if (jsonList[i].code == 'moving_max_dis') {
|
||||
_moving_max_dis = jsonList[i].value ?? 0;
|
||||
} else if (jsonList[i].code == 'scene')
|
||||
_spaceType = getSpaceType(jsonList[i].value ?? 'none');
|
||||
}
|
||||
return CeilingSensorModel(
|
||||
spaceType: _spaceType,
|
||||
movingMaxDis: _moving_max_dis,
|
||||
presenceState: _presenceState,
|
||||
sensitivity: _sensitivity,
|
||||
checkingResult: _checkingResult,
|
||||
presenceRange: _presenceRange,
|
||||
sportsPara: _sportsPara,
|
||||
nobodyTime: _nobody_time,
|
||||
bodyMovement: _bodyMovement);
|
||||
}
|
||||
}
|
||||
|
||||
enum SpaceTypes {
|
||||
none,
|
||||
parlour,
|
||||
area,
|
||||
toilet,
|
||||
bedroom,
|
||||
}
|
||||
|
||||
SpaceTypes getSpaceType(String value) {
|
||||
switch (value) {
|
||||
case 'parlour':
|
||||
return SpaceTypes.parlour;
|
||||
case 'area':
|
||||
return SpaceTypes.area;
|
||||
case 'toilet':
|
||||
return SpaceTypes.toilet;
|
||||
case 'bedroom':
|
||||
return SpaceTypes.bedroom;
|
||||
case 'none':
|
||||
default:
|
||||
return SpaceTypes.none;
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ class AcInterface extends StatelessWidget {
|
||||
},
|
||||
builder: (context, state) {
|
||||
AcStatusModel statusModel = AcStatusModel(
|
||||
countdown1: 0,
|
||||
uuid: ac.uuid ?? '',
|
||||
acSwitch: true,
|
||||
modeString: 'hot',
|
||||
|
@ -7,11 +7,13 @@ import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_state.dart';
|
||||
import 'package:syncrow_app/features/devices/model/ac_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/ACs/ac_mode_control_unit.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/ACs/ac_timer_page.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
|
||||
class AcInterfaceControls extends StatelessWidget {
|
||||
const AcInterfaceControls({super.key, required this.deviceModel, required this.deviceStatus});
|
||||
const AcInterfaceControls(
|
||||
{super.key, required this.deviceModel, required this.deviceStatus});
|
||||
|
||||
final DeviceModel deviceModel;
|
||||
final AcStatusModel deviceStatus;
|
||||
@ -20,8 +22,9 @@ class AcInterfaceControls extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<ACsBloc, AcsState>(
|
||||
builder: (context, state) {
|
||||
String lockIconName =
|
||||
deviceStatus.childLock ? Assets.assetsIconsLock : Assets.assetsIconsUnLock;
|
||||
String lockIconName = deviceStatus.childLock
|
||||
? Assets.assetsIconsLock
|
||||
: Assets.assetsIconsUnLock;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
@ -34,11 +37,22 @@ class AcInterfaceControls extends StatelessWidget {
|
||||
children: [
|
||||
Flexible(
|
||||
child: GestureDetector(
|
||||
onTap: () {},
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
PageRouteBuilder(
|
||||
pageBuilder: (context, animation1, animation2) =>
|
||||
AcTimerPage(
|
||||
device: deviceModel,
|
||||
deviceCode: deviceModel.type!,
|
||||
switchCode: '',
|
||||
)));
|
||||
},
|
||||
child: DefaultContainer(
|
||||
height: 55,
|
||||
child: Center(
|
||||
child: SvgPicture.asset(Assets.assetsIconsAutomatedClock),
|
||||
child:
|
||||
SvgPicture.asset(Assets.assetsIconsAutomatedClock),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
168
lib/features/devices/view/widgets/ACs/ac_timer_page.dart
Normal file
168
lib/features/devices/view/widgets/ACs/ac_timer_page.dart
Normal file
@ -0,0 +1,168 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/6_scene_switch_bloc/6_scene_state.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_event.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/acs_bloc/acs_state.dart';
|
||||
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/ACs/custom_halfhour_timer_picker.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/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
|
||||
class AcTimerPage extends StatelessWidget {
|
||||
final DeviceModel device;
|
||||
final String deviceCode;
|
||||
final String switchCode;
|
||||
const AcTimerPage(
|
||||
{required this.device,
|
||||
required this.deviceCode,
|
||||
required this.switchCode,
|
||||
super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnnotatedRegion(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
),
|
||||
child: BlocProvider(
|
||||
create: (context) => ACsBloc(acId: device.uuid ?? ''),
|
||||
child: BlocBuilder<ACsBloc, AcsState>(
|
||||
builder: (context, state) {
|
||||
final oneGangBloc = BlocProvider.of<ACsBloc>(context);
|
||||
Duration duration = Duration.zero;
|
||||
int selectedValue = 0;
|
||||
|
||||
int countNum = 0;
|
||||
if (state is UpdateTimerState) {
|
||||
countNum = state.seconds;
|
||||
} else if (state is TimerRunInProgress) {
|
||||
countNum = state.remainingTime;
|
||||
} else if (state is TimerRunComplete) {
|
||||
countNum = 0;
|
||||
}
|
||||
// else if (state is LoadingNewSate) {
|
||||
// countNum = 0;
|
||||
// }
|
||||
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (didPop) {
|
||||
if (!didPop) {
|
||||
oneGangBloc.add(OnClose());
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: DefaultTabController(
|
||||
length: 2,
|
||||
child: DefaultScaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
centerTitle: true,
|
||||
title: const BodyLarge(
|
||||
text: 'Countdown',
|
||||
fontColor: ColorsManager.primaryColor,
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
),
|
||||
child: state is AcsLoadingState
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
decoration: const ShapeDecoration(
|
||||
color: ColorsManager.onPrimaryColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(30)),
|
||||
),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Container(
|
||||
child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
countNum > 0
|
||||
? BodyLarge(
|
||||
text: formatDuration(countNum),
|
||||
fontColor: ColorsManager
|
||||
.slidingBlueColor,
|
||||
fontSize: 40,
|
||||
)
|
||||
: Container(
|
||||
child: CustomHalfHourPicker(
|
||||
onValueChanged: (value) {
|
||||
selectedValue =
|
||||
(value * 10).toInt();
|
||||
if (selectedValue == 5) {
|
||||
duration = const Duration(
|
||||
minutes: 30);
|
||||
countNum =
|
||||
duration.inSeconds;
|
||||
} else {
|
||||
duration = Duration(
|
||||
minutes:
|
||||
selectedValue *
|
||||
6);
|
||||
countNum =
|
||||
duration.inSeconds;
|
||||
}
|
||||
print(
|
||||
"Selected Value: $selectedValue, Duration: $duration");
|
||||
},
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (state is LoadingNewSate) {
|
||||
return;
|
||||
}
|
||||
if (countNum > 0) {
|
||||
oneGangBloc.add(SetCounterValue(
|
||||
seconds: countNum,
|
||||
deviceCode:'countdown_time',
|
||||
duration: selectedValue));
|
||||
} else if (duration != Duration.zero) {
|
||||
oneGangBloc.add(SetCounterValue(
|
||||
seconds: 0,
|
||||
deviceCode:
|
||||
'countdown_time',
|
||||
duration: selectedValue));
|
||||
}
|
||||
},
|
||||
child: SvgPicture.asset(countNum > 0
|
||||
? Assets.pauseIcon
|
||||
: Assets.playIcon)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)));
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String formatDuration(int seconds) {
|
||||
final hours = (seconds ~/ 3600).toString().padLeft(2, '0');
|
||||
final minutes = ((seconds % 3600) ~/ 60).toString().padLeft(2, '0');
|
||||
final secs = (seconds % 60).toString().padLeft(2, '0');
|
||||
return '$hours:$minutes:$secs';
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomHalfHourPicker extends StatefulWidget {
|
||||
final Function(double) onValueChanged;
|
||||
|
||||
const CustomHalfHourPicker({super.key, required this.onValueChanged});
|
||||
|
||||
@override
|
||||
_CustomHalfHourPickerState createState() => _CustomHalfHourPickerState();
|
||||
}
|
||||
|
||||
class _CustomHalfHourPickerState extends State<CustomHalfHourPicker> {
|
||||
double selectedValue = 0.0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 200,
|
||||
child: ListWheelScrollView.useDelegate(
|
||||
itemExtent: 50,
|
||||
perspective: 0.005,
|
||||
physics: const FixedExtentScrollPhysics(),
|
||||
overAndUnderCenterOpacity: 0.3,
|
||||
onSelectedItemChanged: (index) {
|
||||
setState(() {
|
||||
selectedValue =
|
||||
index * 0.5; // Convert index to half-hour increments
|
||||
widget.onValueChanged(selectedValue);
|
||||
});
|
||||
},
|
||||
childDelegate: ListWheelChildBuilderDelegate(
|
||||
builder: (context, index) {
|
||||
if (index < 0 || index > 48)
|
||||
return null; // Limit to 24.0 (48 * 0.5)
|
||||
return Center(
|
||||
child: Text(
|
||||
(index * 0.5)
|
||||
.toStringAsFixed(1), // Display value as 0.0, 0.5, etc.
|
||||
style: const TextStyle(
|
||||
fontSize: 25,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w800),
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: 49, // 0.0 to 24.0 (inclusive)
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
|
||||
class CeilingHelpDescription extends StatelessWidget {
|
||||
const CeilingHelpDescription({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultScaffold(
|
||||
title: 'Help Description',
|
||||
child: Center(
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
children: [
|
||||
Text(
|
||||
'1. Nobody, the report to someone\n'
|
||||
'Instruction: The propagation and processing of electromagnetic waves are complicated. '
|
||||
'There will be false alarms in the actual use of radar, and there will be some influencing '
|
||||
'factors in the environment, including:\n\n'
|
||||
'A. Physical disturbance: including air conditioning, fan, motor and other facilities vibration, '
|
||||
'cats, dogs, mice, birds and other animals passing by, which may cause radar misjudgement '
|
||||
'of the environment.\n\n'
|
||||
'B. Space electromagnetic wave disturbance: This includes the possible existence of high-power '
|
||||
'electrical equipment around the radar, electromagnetic-wave-intensive places, and the '
|
||||
'simultaneous coexistence of multiple radars and other environmental factors. These '
|
||||
'interferences are relatively few in home and office scenarios but may be more common '
|
||||
'in factories and industrial environments.\n\n'
|
||||
'C. Power supply disturbance: This is mainly caused by power supply radar crosstalk due to '
|
||||
'associated facilities and equipment in the mains environment, resulting in an unstable '
|
||||
'power supply for the radar and potential misjudgement.\n\n'
|
||||
'2. In the case of human misreporting no one:\n'
|
||||
'A. The presence of a human body may be beyond the radar test range.\n\n'
|
||||
'B. The human body is covered by metal or by extremely thick office desks and chairs.\n\n'
|
||||
'C. When sleeping, the body might not exhibit noticeable breathing micro-movements on the side, '
|
||||
'leading to a short-term misjudgement as nobody.',
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -7,6 +7,11 @@ import 'package:syncrow_app/features/devices/bloc/ceiling_bloc/ceiling_sensor_ev
|
||||
import 'package:syncrow_app/features/devices/bloc/ceiling_bloc/ceiling_sensor_state.dart';
|
||||
import 'package:syncrow_app/features/devices/model/ceiling_sensor_model.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/ceiling_sensor/ceiling_help_description.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/ceiling_sensor/double_parameter_control.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/ceiling_sensor/max_distance_control.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/ceiling_sensor/presence_record.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/ceiling_sensor/presence_space_type.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/device_appbar.dart';
|
||||
import 'package:syncrow_app/features/devices/view/widgets/wall_sensor/wall_sensor_interface.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
|
||||
@ -22,382 +27,375 @@ import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
|
||||
class CeilingSensorInterface extends StatelessWidget {
|
||||
const CeilingSensorInterface({super.key, required this.ceilingSensor});
|
||||
|
||||
final DeviceModel ceilingSensor;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// String state = ceilingSensor.status
|
||||
// .firstWhere((element) => element.code == "presence_state")
|
||||
// .value
|
||||
// .toString();
|
||||
|
||||
return BlocProvider(
|
||||
create: (context) =>
|
||||
CeilingSensorBloc(deviceId: ceilingSensor.uuid ?? '')..add(InitialEvent()),
|
||||
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(builder: (context, state) {
|
||||
CeilingSensorModel ceilingSensorModel = CeilingSensorModel(
|
||||
presenceState: 'none',
|
||||
sensitivity: 1,
|
||||
checkingResult: '',
|
||||
presenceRange: 1,
|
||||
sportsPara: 1,
|
||||
bodyMovement: 'none');
|
||||
if (state is UpdateState) {
|
||||
ceilingSensorModel = state.ceilingSensorModel;
|
||||
} else if (state is LoadingNewSate) {
|
||||
ceilingSensorModel = state.ceilingSensorModel;
|
||||
}
|
||||
return AnnotatedRegion(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
),
|
||||
child: Scaffold(
|
||||
backgroundColor: ColorsManager.backgroundColor,
|
||||
extendBodyBehindAppBar: true,
|
||||
extendBody: true,
|
||||
appBar: DeviceAppbar(
|
||||
deviceName: ceilingSensor.name!,
|
||||
deviceUuid: ceilingSensor.uuid!,
|
||||
create: (context) => CeilingSensorBloc(deviceId: ceilingSensor.uuid ?? '')
|
||||
..add(InitialEvent()),
|
||||
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
|
||||
builder: (context, state) {
|
||||
final _bloc = BlocProvider.of<CeilingSensorBloc>(context);
|
||||
|
||||
CeilingSensorModel ceilingSensorModel = _initializeSensorModel(state);
|
||||
|
||||
return AnnotatedRegion(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarColor: ColorsManager.primaryColor.withOpacity(0.5),
|
||||
statusBarIconBrightness: Brightness.light,
|
||||
),
|
||||
body: Container(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
height: MediaQuery.sizeOf(context).height,
|
||||
padding: const EdgeInsets.all(Constants.defaultPadding),
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(
|
||||
Assets.assetsImagesBackground,
|
||||
),
|
||||
fit: BoxFit.cover,
|
||||
opacity: 0.4,
|
||||
),
|
||||
),
|
||||
child: state is LoadingInitialState
|
||||
? const Center(
|
||||
child: RefreshProgressIndicator(),
|
||||
)
|
||||
: SafeArea(
|
||||
child: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
BlocProvider.of<CeilingSensorBloc>(context).add(InitialEvent());
|
||||
},
|
||||
child: Container(
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
Container(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
// InkWell(
|
||||
// onTap: () {
|
||||
// if ((ceilingSensor.isOnline ?? false) == false) {
|
||||
// ScaffoldMessenger.of(context).showSnackBar(
|
||||
// const SnackBar(
|
||||
// content: Text(
|
||||
// 'Device is offline',
|
||||
// ),
|
||||
// backgroundColor: Colors.red,
|
||||
// ),
|
||||
// );
|
||||
// return;
|
||||
// }
|
||||
// String controlCode = 'sensitivity';
|
||||
// showDialog(
|
||||
// context: context,
|
||||
// builder: (context) => ParameterControlDialog(
|
||||
// title: 'Sensitivity',
|
||||
// sensor: ceilingSensor,
|
||||
// controlCode: controlCode,
|
||||
// value: ceilingSensor.status
|
||||
// .firstWhere((element) => element.code == controlCode)
|
||||
// .value as int,
|
||||
// min: ceilingSensor.functions
|
||||
// .firstWhere((element) => element.code == controlCode)
|
||||
// .values
|
||||
// ?.min ??
|
||||
// 0,
|
||||
// max: ceilingSensor.functions
|
||||
// .firstWhere((element) => element.code == controlCode)
|
||||
// .values
|
||||
// ?.max ??
|
||||
// 0,
|
||||
// ),
|
||||
// );
|
||||
// },
|
||||
// child:
|
||||
// ),
|
||||
|
||||
SvgPicture.asset(
|
||||
ceilingSensorModel.presenceState.toLowerCase() ==
|
||||
'motion'
|
||||
? Assets
|
||||
.assetsIconsPresenceSensorAssetsPresenceSensorMotion
|
||||
: Assets.assetsIconsPresenceSensorAssetsPresence,
|
||||
width: 100,
|
||||
height: 100,
|
||||
// colorFilter: ColorFilter.mode(
|
||||
// (ceilingSensor.isOnline ?? false)
|
||||
// ? ColorsManager.primaryColor
|
||||
// : Colors.grey.withOpacity(0.9),
|
||||
// BlendMode.srcIn,
|
||||
// ),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
BodyMedium(
|
||||
text: StringHelpers.toTitleCase(
|
||||
ceilingSensorModel.presenceState),
|
||||
// (ceilingSensor.isOnline ?? false)
|
||||
// ? StringHelpers.toTitleCase(ceilingSensor.status
|
||||
// .firstWhere((element) =>
|
||||
// element.code == 'presence_state')
|
||||
// .value
|
||||
// .toString())
|
||||
// : "Offline",
|
||||
style: context.bodyMedium.copyWith(
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Column(
|
||||
children: [
|
||||
DefaultContainer(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 20,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const BodySmall(text: 'Sports Para'),
|
||||
BodyLarge(
|
||||
text: ceilingSensorModel.sportsPara
|
||||
.toString(),
|
||||
style: context.bodyLarge.copyWith(
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Container(
|
||||
width: 1,
|
||||
height: 45,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const BodySmall(
|
||||
text: 'Detection Range',
|
||||
textOverflow: TextOverflow.ellipsis,
|
||||
),
|
||||
BodyLarge(
|
||||
text:
|
||||
'${ceilingSensorModel.presenceRange}M',
|
||||
textOverflow: TextOverflow.ellipsis,
|
||||
style: context.bodyLarge.copyWith(
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Container(
|
||||
width: 1,
|
||||
height: 45,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const BodySmall(
|
||||
text: 'Movement',
|
||||
textOverflow: TextOverflow.ellipsis,
|
||||
),
|
||||
BodyLarge(
|
||||
text: ceilingSensorModel.bodyMovement,
|
||||
textOverflow: TextOverflow.ellipsis,
|
||||
style: context.bodyLarge.copyWith(
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
for (int index = 0;
|
||||
index < ceilingSensorButtons().length;
|
||||
index++)
|
||||
DefaultContainer(
|
||||
margin: const EdgeInsets.only(bottom: 5),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12, horizontal: 20),
|
||||
onTap: () async {
|
||||
if (ceilingSensorButtons()[index]['title'] ==
|
||||
'Sensitivity') {
|
||||
final result = await showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return ParameterControlDialog(
|
||||
title: ceilingSensorButtons()[index]
|
||||
['title']
|
||||
.toString(),
|
||||
sensor: ceilingSensor,
|
||||
value: ceilingSensorModel.sensitivity,
|
||||
min: 0,
|
||||
max: 10,
|
||||
);
|
||||
},
|
||||
);
|
||||
if (result != null) {
|
||||
BlocProvider.of<CeilingSensorBloc>(context).add(
|
||||
ChangeValueEvent(
|
||||
value: result, code: 'sensitivity'));
|
||||
}
|
||||
}
|
||||
|
||||
// if (ceilingSensorButtons[index]['page'] != null) {
|
||||
// Navigator.push(
|
||||
// context,
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) =>
|
||||
// ceilingSensorButtons[index]['page'] as Widget,
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
ceilingSensorButtons()[index]['icon']
|
||||
as String,
|
||||
// width: 30,
|
||||
// height: 50,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 25,
|
||||
),
|
||||
BodyMedium(
|
||||
text: ceilingSensorButtons()[index]['title']
|
||||
as String,
|
||||
style: context.bodyMedium.copyWith(
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (ceilingSensorButtons()[index]['withArrow'] ==
|
||||
true)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text(ceilingSensorButtons(
|
||||
sensitivity_val: ceilingSensorModel
|
||||
.sensitivity
|
||||
.toString())[index]['val']
|
||||
.toString()),
|
||||
const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: ColorsManager.greyColor,
|
||||
size: 15,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
))),
|
||||
),
|
||||
);
|
||||
}),
|
||||
child: Scaffold(
|
||||
backgroundColor: ColorsManager.backgroundColor,
|
||||
extendBodyBehindAppBar: true,
|
||||
extendBody: true,
|
||||
appBar: DeviceAppbar(
|
||||
deviceName: ceilingSensor.name!,
|
||||
deviceUuid: ceilingSensor.uuid!,
|
||||
),
|
||||
body: _buildBody(context, state, _bloc, ceilingSensorModel),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
dynamic ceilingSensorButtons({
|
||||
sensitivity_val = 0,
|
||||
CeilingSensorModel _initializeSensorModel(CeilingSensorState state) {
|
||||
CeilingSensorModel ceilingSensorModel = CeilingSensorModel(
|
||||
spaceType: SpaceTypes.none,
|
||||
nobodyTime: '',
|
||||
movingMaxDis: 0,
|
||||
presenceState: 'none',
|
||||
sensitivity: 1,
|
||||
checkingResult: '',
|
||||
presenceRange: 1,
|
||||
sportsPara: 1,
|
||||
bodyMovement: 'none',
|
||||
);
|
||||
|
||||
if (state is UpdateState) {
|
||||
ceilingSensorModel = state.ceilingSensorModel;
|
||||
} else if (state is LoadingNewSate) {
|
||||
ceilingSensorModel = state.ceilingSensorModel;
|
||||
}
|
||||
|
||||
return ceilingSensorModel;
|
||||
}
|
||||
|
||||
Widget _buildBody(BuildContext context, CeilingSensorState state,
|
||||
CeilingSensorBloc bloc, CeilingSensorModel model) {
|
||||
return Container(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
height: MediaQuery.sizeOf(context).height,
|
||||
padding: const EdgeInsets.all(Constants.defaultPadding),
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(Assets.assetsImagesBackground),
|
||||
fit: BoxFit.cover,
|
||||
opacity: 0.4,
|
||||
),
|
||||
),
|
||||
child: state is LoadingInitialState
|
||||
? const Center(child: RefreshProgressIndicator())
|
||||
: SafeArea(
|
||||
child: RefreshIndicator(
|
||||
onRefresh: () async => bloc.add(InitialEvent()),
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
_buildMainContent(context, bloc, model),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMainContent(
|
||||
BuildContext context, CeilingSensorBloc bloc, CeilingSensorModel model) {
|
||||
return SizedBox(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: Column(
|
||||
children: [
|
||||
_buildPresenceDisplay(context, model),
|
||||
Expanded(flex: 3, child: _buildControlButtons(context, bloc, model)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPresenceDisplay(BuildContext context, CeilingSensorModel model) {
|
||||
return Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
model.presenceState.toLowerCase() == 'motion'
|
||||
? Assets.assetsIconsPresenceSensorAssetsPresenceSensorMotion
|
||||
: Assets.assetsIconsPresenceSensorAssetsPresence,
|
||||
width: 100,
|
||||
height: 100,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
BodyMedium(
|
||||
text: StringHelpers.toTitleCase(model.presenceState),
|
||||
style: context.bodyMedium.copyWith(fontWeight: FontsManager.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildControlButtons(
|
||||
BuildContext context, CeilingSensorBloc bloc, CeilingSensorModel model) {
|
||||
return Column(
|
||||
children: [
|
||||
DefaultContainer(
|
||||
padding: const EdgeInsets.symmetric(vertical: 20),
|
||||
child: _buildSensorAttributes(context, model),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
..._buildActionButtons(context, bloc, model),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSensorAttributes(
|
||||
BuildContext context, CeilingSensorModel model) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_buildAttribute(context, 'Sports Para', model.sportsPara.toString()),
|
||||
_divider(),
|
||||
_buildAttribute(context, 'Detection Range', '${model.presenceRange}M'),
|
||||
_divider(),
|
||||
_buildAttribute(context, 'Movement', model.bodyMovement),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAttribute(BuildContext context, String label, String value) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
BodySmall(
|
||||
text: label,
|
||||
),
|
||||
BodyLarge(
|
||||
text: value,
|
||||
style: context.bodyLarge.copyWith(
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _divider() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Container(width: 1, height: 45, color: ColorsManager.greyColor),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildActionButtons(
|
||||
BuildContext context, CeilingSensorBloc bloc, CeilingSensorModel model) {
|
||||
return ceilingSensorButtons().map((button) {
|
||||
return DefaultContainer(
|
||||
margin: const EdgeInsets.only(bottom: 5),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 20),
|
||||
onTap: () => _handleButtonTap(context, bloc, button, model),
|
||||
child: _buildButtonContent(context, button, model),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Widget _buildButtonContent(BuildContext context, Map<String, dynamic> button,
|
||||
CeilingSensorModel model) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SvgPicture.asset(button['icon'] as String),
|
||||
const SizedBox(width: 25),
|
||||
BodyMedium(
|
||||
text: button['title'] as String,
|
||||
style: context.bodyMedium.copyWith(fontWeight: FontsManager.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (button['withArrow'] == true)
|
||||
Row(
|
||||
children: [
|
||||
if (button['title'] == 'Sensitivity') ...[
|
||||
Text(
|
||||
model.sensitivity.toString(),
|
||||
style: const TextStyle(color: Colors.black),
|
||||
),
|
||||
] else if (button['title'] == 'Maximum Distance') ...[
|
||||
Text(
|
||||
model.movingMaxDis.toString(),
|
||||
style: const TextStyle(color: Colors.black),
|
||||
),
|
||||
const Text(
|
||||
"m",
|
||||
style: TextStyle(color: ColorsManager.greyColor),
|
||||
),
|
||||
] else if (button['title'] == 'Nobody Time') ...[
|
||||
Text(
|
||||
model.nobodyTime.toString(),
|
||||
style: const TextStyle(color: Colors.black),
|
||||
),
|
||||
] else if (button['title'] == 'Space Type') ...[
|
||||
Text(
|
||||
model.spaceType.name.toString(),
|
||||
style: const TextStyle(color: Colors.black),
|
||||
),
|
||||
] else ...[
|
||||
Text(
|
||||
button['val'].toString(),
|
||||
style: const TextStyle(color: Colors.black),
|
||||
),
|
||||
],
|
||||
const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: ColorsManager.greyColor,
|
||||
size: 15,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _handleButtonTap(BuildContext context, CeilingSensorBloc bloc,
|
||||
Map<String, dynamic> button, CeilingSensorModel model) async {
|
||||
final title = button['title'];
|
||||
if (title == 'Space Type') {
|
||||
final result = await _showDialog(
|
||||
context,
|
||||
PresenceSpaceTypeDialog(
|
||||
description: 'Space Type',
|
||||
onSpaceTypeSelected: (spaceType) {
|
||||
bloc.add(ChangeValueEvent(
|
||||
type: title.toString(), value: spaceType.name, code: 'scene'));
|
||||
},
|
||||
selectedSpaceType: model.spaceType,
|
||||
),
|
||||
);
|
||||
} else if (title == 'Nobody Time') {
|
||||
final result = await _showDialog(
|
||||
context,
|
||||
MaxDistanceControl(
|
||||
title: title.toString(),
|
||||
sensor: ceilingSensor,
|
||||
value: model.nobodyTime,
|
||||
min: 0.0,
|
||||
max: 5.0,
|
||||
measurement: '',
|
||||
),
|
||||
);
|
||||
if (result != null) {
|
||||
bloc.add(ChangeValueEvent(
|
||||
type: title.toString(), value: result, code: 'nobody_time'));
|
||||
}
|
||||
} else if (title == 'Maximum Distance') {
|
||||
final result = await _showDialog(
|
||||
context,
|
||||
DoubleParameterControl(
|
||||
title: title.toString(),
|
||||
sensor: ceilingSensor,
|
||||
value: model.movingMaxDis,
|
||||
min: 0,
|
||||
max: 500,
|
||||
measurement: 'm',
|
||||
),
|
||||
);
|
||||
if (result != null) {
|
||||
bloc.add(ChangeValueEvent(
|
||||
type: title.toString(), value: result, code: 'moving_max_dis'));
|
||||
}
|
||||
} else if (title == 'Sensitivity') {
|
||||
final result = await _showDialog(
|
||||
context,
|
||||
ParameterControlDialog(
|
||||
title: title.toString(),
|
||||
sensor: ceilingSensor,
|
||||
value: model.sensitivity,
|
||||
min: 0,
|
||||
max: 10,
|
||||
));
|
||||
if (result != null) {
|
||||
bloc.add(ChangeValueEvent(
|
||||
type: title.toString(), value: result, code: 'sensitivity'));
|
||||
}
|
||||
} else if (title == 'Help Description') {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const CeilingHelpDescription()),
|
||||
);
|
||||
} else if (title == 'Induction History') {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PresenceRecord(uuid: ceilingSensor.uuid!)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<dynamic> _showDialog(BuildContext context, Widget dialog) {
|
||||
return showDialog(context: context, builder: (context) => dialog);
|
||||
}
|
||||
|
||||
List<Map<String, dynamic>> ceilingSensorButtons({
|
||||
sensitivityVal = 0,
|
||||
spaceType = 'Office',
|
||||
maximumDistanceVal = '3.9m',
|
||||
nobodyTimeVal = '1hr',
|
||||
}) =>
|
||||
[
|
||||
{
|
||||
'title': 'Space Type',
|
||||
'icon': Assets.spaceTypeIcon,
|
||||
'page': null,
|
||||
'withArrow': true,
|
||||
'val': spaceType
|
||||
},
|
||||
{
|
||||
'title': 'Sensitivity',
|
||||
'icon': Assets.sensitivityIcon,
|
||||
'page': null,
|
||||
'withArrow': true,
|
||||
'val': sensitivity_val
|
||||
},
|
||||
{
|
||||
'title': 'Maximum Distance',
|
||||
'icon': Assets.maximumDistanceIcon,
|
||||
'page': null,
|
||||
'withArrow': true,
|
||||
'val': maximumDistanceVal,
|
||||
},
|
||||
{
|
||||
'title': 'Nobody Time',
|
||||
'icon': Assets.assetsIconsPresenceSensorAssetsEmpty,
|
||||
'page': null,
|
||||
'withArrow': true,
|
||||
'val': nobodyTimeVal,
|
||||
},
|
||||
{
|
||||
'title': 'Induction History',
|
||||
'icon': Assets.assetsIconsPresenceSensorAssetsInductionRecording,
|
||||
'page': null,
|
||||
'withArrow': false,
|
||||
},
|
||||
{
|
||||
'title': 'Help Description',
|
||||
'icon': Assets.assetsIconsPresenceSensorAssetsHelpDescription,
|
||||
'page': null,
|
||||
'withArrow': false,
|
||||
},
|
||||
];
|
||||
}) {
|
||||
return [
|
||||
{
|
||||
'title': 'Space Type',
|
||||
'icon': Assets.spaceTypeIcon,
|
||||
'page': null,
|
||||
'withArrow': true,
|
||||
'val': spaceType,
|
||||
},
|
||||
{
|
||||
'title': 'Sensitivity',
|
||||
'icon': Assets.sensitivityIcon,
|
||||
'page': null,
|
||||
'withArrow': true,
|
||||
'val': sensitivityVal,
|
||||
},
|
||||
{
|
||||
'title': 'Maximum Distance',
|
||||
'icon': Assets.maximumDistanceIcon,
|
||||
'page': null,
|
||||
'withArrow': true,
|
||||
'val': maximumDistanceVal,
|
||||
},
|
||||
{
|
||||
'title': 'Nobody Time',
|
||||
'icon': Assets.assetsIconsPresenceSensorAssetsEmpty,
|
||||
'page': null,
|
||||
'withArrow': true,
|
||||
'val': nobodyTimeVal,
|
||||
},
|
||||
{
|
||||
'title': 'Induction History',
|
||||
'icon': Assets.assetsIconsPresenceSensorAssetsInductionRecording,
|
||||
'page': null,
|
||||
'withArrow': false,
|
||||
},
|
||||
{
|
||||
'title': 'Help Description',
|
||||
'icon': Assets.assetsIconsPresenceSensorAssetsHelpDescription,
|
||||
'page': null,
|
||||
'withArrow': false,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,201 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
|
||||
import 'package:syncrow_app/utils/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
|
||||
class DoubleParameterControl extends StatefulWidget {
|
||||
final String title;
|
||||
final String measurement;
|
||||
final DeviceModel sensor;
|
||||
final int value;
|
||||
final int min;
|
||||
final int max;
|
||||
|
||||
const DoubleParameterControl({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.measurement,
|
||||
required this.sensor,
|
||||
required this.value,
|
||||
required this.min,
|
||||
required this.max,
|
||||
});
|
||||
|
||||
@override
|
||||
DoubleParameterControlState createState() => DoubleParameterControlState();
|
||||
}
|
||||
|
||||
class DoubleParameterControlState extends State<DoubleParameterControl> {
|
||||
late int _value;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_value = widget.value.clamp(widget.min, widget.max);
|
||||
}
|
||||
|
||||
int? _getDivisions(int min, int max, int step) {
|
||||
final range = max - min;
|
||||
final divisions = (range / step).floor();
|
||||
return divisions > 0 ? divisions : null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
BodyMedium(
|
||||
text: widget.title,
|
||||
style: context.bodyMedium.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
fontWeight: FontsManager.extraBold,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 15,
|
||||
horizontal: 50,
|
||||
),
|
||||
child: Container(
|
||||
height: 1,
|
||||
width: double.infinity,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
TitleMedium(
|
||||
text: _value.toStringAsFixed(0),
|
||||
style: context.titleMedium.copyWith(
|
||||
color: Colors.black,
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
),
|
||||
TitleMedium(
|
||||
text: widget.measurement.toString(),
|
||||
style: context.titleMedium.copyWith(
|
||||
color: Colors.black,
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_value = (_value - 50).clamp(widget.min, widget.max);
|
||||
});
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.remove,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Slider(
|
||||
value: _value.toDouble(),
|
||||
min: widget.min.toDouble(),
|
||||
max: widget.max.toDouble(),
|
||||
divisions: _getDivisions(
|
||||
widget.min, widget.max, 50),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_value =
|
||||
value.round();
|
||||
});
|
||||
},
|
||||
label: _value.toStringAsFixed(0),
|
||||
inactiveColor: ColorsManager.greyColor,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_value = (_value + 50).clamp(widget.min, widget.max);
|
||||
});
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.add,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 1,
|
||||
width: double.infinity,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Center(
|
||||
child: BodyMedium(
|
||||
text: 'Cancel',
|
||||
style: context.bodyMedium
|
||||
.copyWith(color: ColorsManager.greyColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 50,
|
||||
width: 1,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Navigator.pop(context, _value);
|
||||
if (widget.sensor.isOnline == null) {
|
||||
CustomSnackBar.displaySnackBar('The device is offline');
|
||||
return;
|
||||
}
|
||||
if (!widget.sensor.isOnline!) {
|
||||
CustomSnackBar.displaySnackBar('The device is offline');
|
||||
return;
|
||||
}
|
||||
},
|
||||
child: Center(
|
||||
child: BodyMedium(
|
||||
text: 'Confirm',
|
||||
style: context.bodyMedium.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,248 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_model.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
|
||||
import 'package:syncrow_app/utils/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
|
||||
class MaxDistanceControl extends StatefulWidget {
|
||||
final String title;
|
||||
final String measurement;
|
||||
final DeviceModel sensor;
|
||||
final String value;
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
|
||||
const MaxDistanceControl({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.measurement,
|
||||
required this.sensor,
|
||||
required this.value,
|
||||
required this.min,
|
||||
required this.max,
|
||||
});
|
||||
|
||||
@override
|
||||
MaxDistanceControlState createState() => MaxDistanceControlState();
|
||||
}
|
||||
|
||||
int _parseValue(String value) {
|
||||
if (value.endsWith('sec')) {
|
||||
return int.parse(value.replaceAll('sec', '').trim());
|
||||
} else if (value.endsWith('min')) {
|
||||
return int.parse(value.replaceAll('min', '').trim()) * 60;
|
||||
} else if (value.endsWith('hr')) {
|
||||
return int.parse(value.replaceAll('hr', '').trim()) * 3600;
|
||||
}
|
||||
return 0; // Default to 0 if the format is unrecognized
|
||||
}
|
||||
|
||||
class MaxDistanceControlState extends State<MaxDistanceControl> {
|
||||
final List<double> _stepValues = [
|
||||
0,
|
||||
10,
|
||||
30,
|
||||
60,
|
||||
120,
|
||||
300,
|
||||
600,
|
||||
1800,
|
||||
3600,
|
||||
];
|
||||
|
||||
late int _currentIndex;
|
||||
|
||||
String _formatLabel(double seconds) {
|
||||
if (seconds == 0) return 'None';
|
||||
if (seconds < 60) return '${seconds.toInt()}sec';
|
||||
if (seconds < 3600) {
|
||||
final minutes = (seconds / 60).round();
|
||||
return '${minutes}min';
|
||||
}
|
||||
final hours = (seconds / 3600).round();
|
||||
return '${hours}hr';
|
||||
}
|
||||
|
||||
int _nearestStepIndex(double initialValue) {
|
||||
double minDiff = double.infinity;
|
||||
int nearestIndex = 0;
|
||||
for (int i = 0; i < _stepValues.length; i++) {
|
||||
final diff = (initialValue - _stepValues[i]).abs();
|
||||
if (diff < minDiff) {
|
||||
minDiff = diff;
|
||||
nearestIndex = i;
|
||||
}
|
||||
}
|
||||
return nearestIndex;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final int parsedSeconds = _parseValue(widget.value);
|
||||
_currentIndex = _nearestStepIndex(parsedSeconds.toDouble());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double currentSeconds = _stepValues[_currentIndex];
|
||||
|
||||
return Dialog(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
BodyMedium(
|
||||
text: widget.title,
|
||||
style: context.bodyMedium.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
fontWeight: FontsManager.extraBold,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 15,
|
||||
horizontal: 50,
|
||||
),
|
||||
child: Container(
|
||||
height: 1,
|
||||
width: double.infinity,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
TitleMedium(
|
||||
text: _formatLabel(currentSeconds),
|
||||
style: context.titleMedium.copyWith(
|
||||
color: Colors.black,
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
TitleMedium(
|
||||
text: widget.measurement,
|
||||
style: context.titleMedium.copyWith(
|
||||
color: Colors.black,
|
||||
fontWeight: FontsManager.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_currentIndex =
|
||||
(_currentIndex > 0) ? _currentIndex - 1 : 0;
|
||||
});
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.remove,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Slider(
|
||||
min: 0,
|
||||
max: (_stepValues.length - 1).toDouble(),
|
||||
divisions: _stepValues.length - 1,
|
||||
value: _currentIndex.toDouble(),
|
||||
onChanged: (double newIndex) {
|
||||
setState(() {
|
||||
_currentIndex = newIndex.round();
|
||||
});
|
||||
},
|
||||
label: _formatLabel(_stepValues[_currentIndex]),
|
||||
inactiveColor: ColorsManager.greyColor,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_currentIndex = (_currentIndex < _stepValues.length - 1)
|
||||
? _currentIndex + 1
|
||||
: _currentIndex;
|
||||
});
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.add,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 1,
|
||||
width: double.infinity,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => Navigator.pop(context),
|
||||
child: Center(
|
||||
child: BodyMedium(
|
||||
text: 'Cancel',
|
||||
style: context.bodyMedium.copyWith(
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 50,
|
||||
width: 1,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
Navigator.pop(context, _formatLabel(currentSeconds));
|
||||
|
||||
if (widget.sensor.isOnline == null ||
|
||||
widget.sensor.isOnline == false) {
|
||||
CustomSnackBar.displaySnackBar('The device is offline');
|
||||
return;
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Center(
|
||||
child: BodyMedium(
|
||||
text: 'Confirm',
|
||||
style: context.bodyMedium.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/ceiling_bloc/ceiling_sensor_bloc.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/ceiling_bloc/ceiling_sensor_event.dart';
|
||||
import 'package:syncrow_app/features/devices/bloc/ceiling_bloc/ceiling_sensor_state.dart';
|
||||
import 'package:syncrow_app/features/devices/model/device_report_model.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/utils/resource_manager/color_manager.dart';
|
||||
|
||||
class PresenceRecord extends StatelessWidget {
|
||||
final String uuid;
|
||||
const PresenceRecord({super.key, required this.uuid});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultScaffold(
|
||||
title: 'Presence Record',
|
||||
child: BlocProvider(
|
||||
create: (context) =>
|
||||
CeilingSensorBloc(deviceId: uuid)..add(const ReportLogsInitial()),
|
||||
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
|
||||
builder: (context, state) {
|
||||
final garageDoorBloc = BlocProvider.of<CeilingSensorBloc>(context);
|
||||
final Map<String, List<DeviceEvent>> groupedRecords = {};
|
||||
|
||||
if (state is LoadingInitialState) {
|
||||
return const Center(
|
||||
child: DefaultContainer(
|
||||
width: 50, height: 50, child: CircularProgressIndicator()),
|
||||
);
|
||||
} else if (state is FitchData) {
|
||||
for (var record in garageDoorBloc.recordGroups.data!) {
|
||||
final DateTime eventDateTime =
|
||||
DateTime.fromMillisecondsSinceEpoch(record.eventTime!);
|
||||
final String formattedDate =
|
||||
DateFormat('EEEE, dd/MM/yyyy').format(eventDateTime);
|
||||
|
||||
// Group by formatted date
|
||||
if (groupedRecords.containsKey(formattedDate)) {
|
||||
groupedRecords[formattedDate]!.add(record);
|
||||
} else {
|
||||
groupedRecords[formattedDate] = [record];
|
||||
}
|
||||
}
|
||||
}
|
||||
return ListView.builder(
|
||||
itemCount: groupedRecords.length,
|
||||
itemBuilder: (context, index) {
|
||||
final String date = groupedRecords.keys.elementAt(index);
|
||||
final List<DeviceEvent> recordsForDate = groupedRecords[date]!;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 5, top: 10),
|
||||
child: Text(
|
||||
date,
|
||||
style: const TextStyle(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
DefaultContainer(
|
||||
child: Column(
|
||||
children: [
|
||||
...recordsForDate.asMap().entries.map((entry) {
|
||||
final int idx = entry.key;
|
||||
final DeviceEvent record = entry.value;
|
||||
final DateTime eventDateTime =
|
||||
DateTime.fromMillisecondsSinceEpoch(
|
||||
record.eventTime!);
|
||||
final String formattedTime =
|
||||
DateFormat('HH:mm:ss').format(eventDateTime);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Container(
|
||||
child: ListTile(
|
||||
leading: Icon(
|
||||
record.value == 'true'
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
color: record.value == 'true'
|
||||
? Colors.blue
|
||||
: Colors.grey,
|
||||
),
|
||||
title: Text(
|
||||
record.value == 'true'
|
||||
? "Opened"
|
||||
: "Closed",
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
subtitle: Text('$formattedTime'),
|
||||
),
|
||||
),
|
||||
if (idx != recordsForDate.length - 1)
|
||||
const Divider(
|
||||
color: ColorsManager.graysColor,
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_app/features/devices/model/ceiling_sensor_model.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/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
|
||||
|
||||
class PresenceSpaceTypeDialog extends StatefulWidget {
|
||||
final String description;
|
||||
final SpaceTypes selectedSpaceType;
|
||||
final void Function(SpaceTypes spaceType) onSpaceTypeSelected;
|
||||
|
||||
const PresenceSpaceTypeDialog({
|
||||
super.key,
|
||||
required this.description,
|
||||
required this.selectedSpaceType,
|
||||
required this.onSpaceTypeSelected,
|
||||
});
|
||||
|
||||
@override
|
||||
_PresenceSpaceTypeDialogState createState() =>
|
||||
_PresenceSpaceTypeDialogState();
|
||||
}
|
||||
|
||||
class _PresenceSpaceTypeDialogState extends State<PresenceSpaceTypeDialog> {
|
||||
late SpaceTypes _selectedSpaceType;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedSpaceType = widget.selectedSpaceType;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Map<SpaceTypes, String> spaceTypeIcons = {
|
||||
SpaceTypes.none: Assets.office,
|
||||
SpaceTypes.parlour: Assets.parlour,
|
||||
SpaceTypes.area: Assets.dyi,
|
||||
SpaceTypes.toilet: Assets.bathroom,
|
||||
SpaceTypes.bedroom: Assets.bedroom,
|
||||
};
|
||||
|
||||
final Map<SpaceTypes, String> spaceTypeTitles = {
|
||||
SpaceTypes.none: 'None',
|
||||
SpaceTypes.parlour: 'Parlour',
|
||||
SpaceTypes.area: 'Area',
|
||||
SpaceTypes.toilet: 'Toilet',
|
||||
SpaceTypes.bedroom: 'Bedroom',
|
||||
};
|
||||
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Container(
|
||||
// padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
widget.description,
|
||||
style: context.bodyMedium.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
fontWeight: FontsManager.extraBold,
|
||||
fontSize: 16),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 1,
|
||||
width: double.infinity,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Wrap(
|
||||
runSpacing: 12,
|
||||
spacing: 16,
|
||||
children: spaceTypeIcons.entries.map((entry) {
|
||||
final spaceType = entry.key;
|
||||
final icon = entry.value;
|
||||
final title = spaceTypeTitles[spaceType]!;
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
color: _selectedSpaceType == spaceType
|
||||
? ColorsManager.primaryColorWithOpacity
|
||||
: ColorsManager.textGray,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_selectedSpaceType = spaceType;
|
||||
});
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 10, right: 10),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: SvgPicture.asset(
|
||||
icon,
|
||||
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
title,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(
|
||||
color: _selectedSpaceType == spaceType
|
||||
? ColorsManager.onPrimaryColor
|
||||
: ColorsManager.blackColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 15),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
Container(
|
||||
height: 1,
|
||||
width: double.infinity,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
|
||||
/// Cancel / Confirm
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => Navigator.pop(context),
|
||||
child: Center(
|
||||
child: BodyMedium(
|
||||
text: 'Cancel',
|
||||
style: context.bodyMedium.copyWith(
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 50,
|
||||
width: 1,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
widget.onSpaceTypeSelected(_selectedSpaceType);
|
||||
Navigator.pop(context, _selectedSpaceType);
|
||||
},
|
||||
child: Center(
|
||||
child: BodyMedium(
|
||||
text: 'Confirm',
|
||||
style: context.bodyMedium.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -57,9 +57,11 @@ class JoinHomeView extends StatelessWidget {
|
||||
'Please enter the invitation code');
|
||||
return;
|
||||
}
|
||||
if (await HomeCubit.getInstance()
|
||||
.joinAUnit(textEditingController.text)) {
|
||||
if (await HomeCubit.getInstance().activationCode(textEditingController.text)) {
|
||||
await HomeCubit.getInstance().fetchUnitsByUserId();
|
||||
|
||||
CustomSnackBar.displaySnackBar('Done successfully');
|
||||
|
||||
Navigator.of(context).pop();
|
||||
} else {
|
||||
CustomSnackBar.displaySnackBar('Wrong code!');
|
||||
|
199
lib/features/menu/view/widgets/securty/bloc/security_bloc.dart
Normal file
199
lib/features/menu/view/widgets/securty/bloc/security_bloc.dart
Normal file
@ -0,0 +1,199 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_event.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_state.dart';
|
||||
import 'package:syncrow_app/services/api/authentication_api.dart';
|
||||
|
||||
class SecurityBloc extends Bloc<SecurityEvent, SecurityState> {
|
||||
bool _isPasswordVisible = false;
|
||||
String otpCode = '';
|
||||
String validate = '';
|
||||
SecurityBloc() : super(PasswordVisibilityState(false)) {
|
||||
on<SetPassword>(_onSetPassword);
|
||||
on<TogglePasswordVisibility>(_onTogglePasswordVisibility);
|
||||
on<StartTimerEvent>(_onStartTimer);
|
||||
on<StopTimerEvent>(_onStopTimer);
|
||||
on<UpdateTimerEvent>(_onUpdateTimer);
|
||||
on<VerifyPassCodeEvent>(verifyCode);
|
||||
on<ChangePasswordEvent>(changePassword);
|
||||
}
|
||||
|
||||
void _onSetPassword(SetPassword event, Emitter<SecurityState> emit) {
|
||||
if (event.password.length < 6) {
|
||||
emit(PasswordErrorState('Password must be at least 6 characters long.'));
|
||||
} else {
|
||||
emit(PasswordSetState('Password successfully set.'));
|
||||
}
|
||||
}
|
||||
|
||||
void _onTogglePasswordVisibility(
|
||||
TogglePasswordVisibility event, Emitter<SecurityState> emit) {
|
||||
_isPasswordVisible = !_isPasswordVisible;
|
||||
emit(PasswordVisibilityState(_isPasswordVisible));
|
||||
}
|
||||
|
||||
String? passwordValidator(String? value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Please enter your password';
|
||||
}
|
||||
List<String> validationErrors = [];
|
||||
|
||||
if (!RegExp(r'^(?=.*[a-z])').hasMatch(value)) {
|
||||
validationErrors.add(' - one lowercase letter');
|
||||
}
|
||||
if (!RegExp(r'^(?=.*[A-Z])').hasMatch(value)) {
|
||||
validationErrors.add(' - one uppercase letter');
|
||||
}
|
||||
if (!RegExp(r'^(?=.*\d)').hasMatch(value)) {
|
||||
validationErrors.add(' - one number');
|
||||
}
|
||||
if (!RegExp(r'^(?=.*[@$!%*?&])').hasMatch(value)) {
|
||||
validationErrors.add(' - one special character');
|
||||
}
|
||||
if (value.length < 8) {
|
||||
validationErrors.add(' - minimum 8 characters');
|
||||
}
|
||||
|
||||
if (validationErrors.isNotEmpty) {
|
||||
return 'Password must contain at least:\n${validationErrors.join('\n')}';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
TextEditingController newPassword = TextEditingController();
|
||||
|
||||
Timer? _timer;
|
||||
int _remainingTime = 0;
|
||||
|
||||
Future _onStartTimer(
|
||||
StartTimerEvent event, Emitter<SecurityState> emit) async {
|
||||
if (_timer != null && _timer!.isActive) {
|
||||
return;
|
||||
}
|
||||
_remainingTime = 1;
|
||||
add(UpdateTimerEvent(
|
||||
remainingTime: _remainingTime, isButtonEnabled: false));
|
||||
try {
|
||||
_remainingTime = 30;
|
||||
var res = (await AuthenticationAPI.sendOtp(body: {
|
||||
'email': HomeCubit.user!.email,
|
||||
'type': 'PASSWORD',
|
||||
if (HomeCubit.user!.regionUuid != null)
|
||||
'regionUuid': HomeCubit.user!.regionUuid
|
||||
}));
|
||||
_remainingTime = res['cooldown'];
|
||||
} on DioException catch (e) {
|
||||
if (e.response!.statusCode == 400) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage = errorData['message'];
|
||||
if (errorMessage == 'User not found') {
|
||||
validate = 'Invalid Credential';
|
||||
emit(AuthInitialState());
|
||||
return 1;
|
||||
} else {
|
||||
validate = '';
|
||||
_remainingTime = errorData['data']['cooldown'] ?? 1;
|
||||
emit(AuthInitialState());
|
||||
}
|
||||
} else {
|
||||
emit(AuthInitialState());
|
||||
|
||||
return 1;
|
||||
}
|
||||
emit(AuthInitialState());
|
||||
} catch (e) {
|
||||
emit(AuthInitialState());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
_remainingTime--;
|
||||
if (_remainingTime <= 0) {
|
||||
_timer?.cancel();
|
||||
add(UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true));
|
||||
} else {
|
||||
add(UpdateTimerEvent(
|
||||
remainingTime: _remainingTime, isButtonEnabled: false));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _onStopTimer(StopTimerEvent event, Emitter<SecurityState> emit) {
|
||||
_timer?.cancel();
|
||||
emit(TimerState(isButtonEnabled: true, remainingTime: 0));
|
||||
}
|
||||
|
||||
void _onUpdateTimer(UpdateTimerEvent event, Emitter<SecurityState> emit) {
|
||||
emit(TimerState(
|
||||
isButtonEnabled: event.isButtonEnabled,
|
||||
remainingTime: event.remainingTime));
|
||||
}
|
||||
|
||||
String formattedTime(int time) {
|
||||
final int days = (time / 86400).floor(); // 86400 seconds in a day
|
||||
final int hours = ((time % 86400) / 3600).floor();
|
||||
final int minutes = (((time % 86400) % 3600) / 60).floor();
|
||||
final int seconds = (((time % 86400) % 3600) % 60).floor();
|
||||
|
||||
final String formattedTime = [
|
||||
if (days > 0) '${days}d', // Append 'd' for days
|
||||
if (days > 0 || hours > 0)
|
||||
hours
|
||||
.toString()
|
||||
.padLeft(2, '0'), // Show hours if there are days or hours
|
||||
minutes.toString().padLeft(2, '0'),
|
||||
seconds.toString().padLeft(2, '0'),
|
||||
].join(':');
|
||||
|
||||
return formattedTime;
|
||||
}
|
||||
|
||||
Future<void> verifyCode(
|
||||
VerifyPassCodeEvent event, Emitter<SecurityState> emit) async {
|
||||
emit(LoadingForgetState());
|
||||
try {
|
||||
final response = await AuthenticationAPI.verifyPassCode(body: {
|
||||
'email': HomeCubit.user!.email!,
|
||||
'type': 'PASSWORD',
|
||||
'otpCode': otpCode
|
||||
});
|
||||
if (response['statusCode'] == 200) {
|
||||
_timer?.cancel();
|
||||
emit(SuccessForgetState());
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage =
|
||||
errorData['error']['message'] ?? 'something went wrong';
|
||||
validate = errorMessage;
|
||||
emit(AuthInitialState());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> changePassword(
|
||||
ChangePasswordEvent event, Emitter<SecurityState> emit) async {
|
||||
emit(LoadingForgetState());
|
||||
try {
|
||||
|
||||
final response = await AuthenticationAPI.forgetPassword(
|
||||
email: HomeCubit.user!.email!,
|
||||
otpCode: event.otpCode,
|
||||
password: newPassword.text,
|
||||
);
|
||||
emit(ChangedPassState());
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage =
|
||||
errorData['error']['message'] ?? 'something went wrong';
|
||||
validate = errorMessage;
|
||||
emit(AuthInitialState());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
abstract class SecurityEvent {}
|
||||
|
||||
class SetPassword extends SecurityEvent {
|
||||
final String password;
|
||||
|
||||
SetPassword(this.password);
|
||||
}
|
||||
|
||||
class TogglePasswordVisibility extends SecurityEvent {}
|
||||
|
||||
class SubmitEvent extends SecurityEvent {}
|
||||
|
||||
class StartTimerEvent extends SecurityEvent {}
|
||||
|
||||
class StopTimerEvent extends SecurityEvent {}
|
||||
|
||||
class UpdateTimerEvent extends SecurityEvent {
|
||||
final int remainingTime;
|
||||
final bool isButtonEnabled;
|
||||
UpdateTimerEvent(
|
||||
{required this.remainingTime, required this.isButtonEnabled});
|
||||
}
|
||||
|
||||
class ChangePasswordEvent extends SecurityEvent {
|
||||
final String otpCode;
|
||||
ChangePasswordEvent({
|
||||
required this.otpCode,
|
||||
});
|
||||
}
|
||||
|
||||
class VerifyPassCodeEvent extends SecurityEvent {}
|
@ -0,0 +1,44 @@
|
||||
abstract class SecurityState {}
|
||||
|
||||
class InitialState extends SecurityState {}
|
||||
|
||||
class PasswordVisibilityState extends SecurityState {
|
||||
final bool isVisible;
|
||||
|
||||
PasswordVisibilityState(this.isVisible);
|
||||
}
|
||||
|
||||
class PasswordSetState extends SecurityState {
|
||||
final String message;
|
||||
|
||||
PasswordSetState(this.message);
|
||||
}
|
||||
|
||||
class PasswordErrorState extends SecurityState {
|
||||
final String error;
|
||||
|
||||
PasswordErrorState(this.error);
|
||||
}
|
||||
|
||||
class AuthTokenLoading extends SecurityState {}
|
||||
|
||||
class AuthLoading extends SecurityState {}
|
||||
|
||||
class AuthInitialState extends SecurityState {}
|
||||
|
||||
class TimerState extends SecurityState {
|
||||
final bool isButtonEnabled;
|
||||
final int remainingTime;
|
||||
|
||||
TimerState({required this.isButtonEnabled, required this.remainingTime});
|
||||
|
||||
@override
|
||||
List<Object> get props => [isButtonEnabled, remainingTime];
|
||||
}
|
||||
class InitialForgetState extends SecurityState {}
|
||||
|
||||
class LoadingForgetState extends SecurityState {}
|
||||
|
||||
class SuccessForgetState extends SecurityState {}
|
||||
class ChangedPassState extends SecurityState {}
|
||||
|
@ -0,0 +1,88 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_bloc.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/view/verification_code_page.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 ChangePasswordPage extends StatelessWidget {
|
||||
const ChangePasswordPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultScaffold(
|
||||
title: 'Change Password',
|
||||
bottomNavBar: SizedBox(
|
||||
height: 150,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
// In your parent widget or navigator
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => BlocProvider(
|
||||
create: (_) => SecurityBloc(), // Provide the Bloc
|
||||
child: const VerificationCodePage(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
height: 50,
|
||||
margin: const EdgeInsets.only(right: 20, left: 20),
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.blueColor,
|
||||
borderRadius: BorderRadius.all(Radius.circular(20))),
|
||||
child: const Center(
|
||||
child: Text(
|
||||
'Get Verification Code',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: ColorsManager.onPrimaryColor),
|
||||
)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 80, bottom: 30),
|
||||
child: SvgPicture.asset(Assets.verificationIcon),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: BodyMedium(
|
||||
text: 'Account Verification',
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
const BodyMedium(
|
||||
text: 'Click here to send a verification ',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
const BodyMedium(
|
||||
text: 'code to your email: test@test.com',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/view/change_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';
|
||||
@ -23,7 +24,11 @@ class SecurtyView extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
onTap: () {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => const ChangePasswordPage(),
|
||||
));
|
||||
},
|
||||
child: const Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
@ -0,0 +1,247 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_bloc.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_event.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_state.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
|
||||
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
||||
class SetPasswordPage extends StatelessWidget {
|
||||
String? otpCode;
|
||||
SetPasswordPage({super.key, this.otpCode});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
return BlocProvider<SecurityBloc>(
|
||||
create: (context) => SecurityBloc(),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: BlocConsumer<SecurityBloc, SecurityState>(
|
||||
listener: (context, state) {
|
||||
if (state is SuccessForgetState) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Change Password Successfully '),
|
||||
),
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
if (state is ChangedPassState) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) {
|
||||
return AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.2,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
BodyLarge(
|
||||
text: 'Password Changed',
|
||||
fontWeight: FontWeight.w700,
|
||||
fontColor:
|
||||
ColorsManager.switchButton.withOpacity(0.6),
|
||||
fontSize: 16,
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(left: 15, right: 15),
|
||||
child: Divider(
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 15, right: 20, top: 15, bottom: 20),
|
||||
child: Column(
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
'Your password has been',
|
||||
textAlign: TextAlign.center,
|
||||
)),
|
||||
Center(
|
||||
child: Text(
|
||||
'successfully updated.',
|
||||
textAlign: TextAlign.center,
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
color: ColorsManager.textGray,
|
||||
width: 1.0,
|
||||
),
|
||||
)),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
AuthCubit.get(context).logout();
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 5, bottom: 5),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Done',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.switchButton
|
||||
.withOpacity(0.6),
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}, builder: (context, state) {
|
||||
final _bloc = BlocProvider.of<SecurityBloc>(context);
|
||||
|
||||
return DefaultScaffold(
|
||||
title: 'Change Password',
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
const SizedBox(height: 55),
|
||||
const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: BodyMedium(
|
||||
text: 'Set Password',
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Center(
|
||||
child: BodyMedium(
|
||||
text: 'Secure your account with a',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
const Center(
|
||||
child: BodyMedium(
|
||||
text: 'strong password',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
BlocBuilder<SecurityBloc, SecurityState>(
|
||||
builder: (context, state) {
|
||||
if (state is PasswordErrorState) {
|
||||
return Center(
|
||||
child: Text(
|
||||
state.error,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
);
|
||||
} else if (state is PasswordSetState) {
|
||||
return Center(
|
||||
child: Text(
|
||||
state.message,
|
||||
style: const TextStyle(color: Colors.green),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
PasswordInputField(
|
||||
controller: _bloc.newPassword,
|
||||
validatorl: _bloc.passwordValidator,
|
||||
),
|
||||
const SizedBox(height: 55),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
_bloc.add(
|
||||
ChangePasswordEvent(otpCode: otpCode.toString()));
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 20, left: 20, top: 15, bottom: 15),
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.blueColor,
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
child: const Center(
|
||||
child: Text(
|
||||
"Done",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
class PasswordInputField extends StatelessWidget {
|
||||
PasswordInputField({this.controller, this.validatorl});
|
||||
TextEditingController? controller = TextEditingController();
|
||||
String? Function(String?)? validatorl;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SecurityBloc, SecurityState>(
|
||||
builder: (context, state) {
|
||||
bool isPasswordVisible = false;
|
||||
if (state is PasswordVisibilityState) {
|
||||
isPasswordVisible = state.isVisible;
|
||||
}
|
||||
|
||||
return TextFormField(
|
||||
controller: controller,
|
||||
obscureText: !isPasswordVisible,
|
||||
validator: validatorl,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Password',
|
||||
suffixIcon: IconButton(
|
||||
icon: SvgPicture.asset(isPasswordVisible
|
||||
? Assets.passwordVisibility
|
||||
: Assets.passwordUnvisibility),
|
||||
onPressed: () {
|
||||
context.read<SecurityBloc>().add(TogglePasswordVisibility());
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
controller!.dispose();
|
||||
}
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_bloc.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_event.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/bloc/security_state.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/view/set_password_page.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/utils/resource_manager/color_manager.dart';
|
||||
import 'package:pin_code_fields/pin_code_fields.dart';
|
||||
|
||||
class VerificationCodePage extends StatelessWidget {
|
||||
const VerificationCodePage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String otp = '';
|
||||
return BlocProvider<SecurityBloc>(
|
||||
create: (context) => SecurityBloc()..add(StartTimerEvent()),
|
||||
child: BlocConsumer<SecurityBloc, SecurityState>(
|
||||
listener: (context, state) {
|
||||
if (state is SuccessForgetState) {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => SetPasswordPage(
|
||||
otpCode: otp,
|
||||
),
|
||||
));
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
final _bloc = BlocProvider.of<SecurityBloc>(context);
|
||||
|
||||
return DefaultScaffold(
|
||||
title: 'Change Password',
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 55),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: BodyMedium(
|
||||
text: 'Verification Code',
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
const BodyMedium(
|
||||
text: 'We have sent the verification code',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const BodyMedium(
|
||||
text: 'to ',
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14,
|
||||
),
|
||||
BodyMedium(
|
||||
text: HomeCubit.user!.email!,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 14,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
BlocBuilder<SecurityBloc, SecurityState>(
|
||||
builder: (context, state) {
|
||||
return Center(
|
||||
child: PinCodeTextField(
|
||||
hintCharacter: '0',
|
||||
appContext: context,
|
||||
length: 6,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
backgroundColor: Colors.transparent,
|
||||
enableActiveFill: true,
|
||||
pinTheme: PinTheme(
|
||||
shape: PinCodeFieldShape.box,
|
||||
activeColor: Colors.white,
|
||||
selectedColor: Colors.white,
|
||||
inactiveColor: Colors.white,
|
||||
inactiveFillColor: Colors.white70,
|
||||
selectedFillColor: Colors.white70,
|
||||
activeFillColor: Colors.white,
|
||||
errorBorderColor: Colors.white,
|
||||
fieldHeight: 55.0,
|
||||
fieldWidth: 55.0,
|
||||
fieldOuterPadding: const EdgeInsets.only(right: 8),
|
||||
borderRadius: BorderRadius.circular(17),
|
||||
borderWidth: 1,
|
||||
),
|
||||
cursorWidth: 1,
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: (value) {
|
||||
// Update OTP code in the bloc when user enters a pin
|
||||
_bloc.otpCode = value;
|
||||
otp = value;
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 55),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: state is TimerState &&
|
||||
!state.isButtonEnabled &&
|
||||
state.remainingTime != 1
|
||||
? null
|
||||
: () {
|
||||
_bloc.add(StartTimerEvent());
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 20, left: 20, top: 15, bottom: 15),
|
||||
decoration: BoxDecoration(
|
||||
color: state is TimerState && !state.isButtonEnabled
|
||||
? ColorsManager.blueButton
|
||||
: ColorsManager.blueColor,
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(20))),
|
||||
child: Center(
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 ? "${_bloc.formattedTime(state.remainingTime)} " : "Resend"}',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: state is TimerState &&
|
||||
!state.isButtonEnabled
|
||||
? Colors.white
|
||||
: ColorsManager.onPrimaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
context
|
||||
.read<SecurityBloc>()
|
||||
.add(VerifyPassCodeEvent());
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 20, left: 20, top: 15, bottom: 15),
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.blueColor,
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(20))),
|
||||
child: const Center(
|
||||
child: Text(
|
||||
"Verify",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w700),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,15 +1,141 @@
|
||||
import 'package:flutter/material.dart';
|
||||
// import 'package:flutter_svg/svg.dart';
|
||||
// import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart';
|
||||
// import 'package:syncrow_app/generated/assets.dart';
|
||||
// import 'package:syncrow_app/navigation/routing_constants.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.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/context_extension.dart';
|
||||
import 'package:syncrow_app/utils/helpers/snack_bar.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
||||
class CreateUnitWidget extends StatelessWidget {
|
||||
const CreateUnitWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container();
|
||||
TextEditingController textEditingController = TextEditingController();
|
||||
return BlocConsumer<HomeCubit, HomeState>(
|
||||
listener: (context, state) {
|
||||
if (state is ActivationError) {}
|
||||
},
|
||||
builder: (context, state) {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
const Text(
|
||||
'Join a Space',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: ColorsManager.secondaryColor),
|
||||
),
|
||||
SizedBox(
|
||||
height: MediaQuery.of(context).size.height * 0.6,
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.assetsIconsMenuIconsHomeManagementIconsJoinAHome,
|
||||
width: 70,
|
||||
height: 70,
|
||||
color: ColorsManager.grayButtonColors,
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 30,
|
||||
),
|
||||
child: BodyMedium(
|
||||
fontWeight: FontWeight.w400,
|
||||
textAlign: TextAlign.center,
|
||||
text: 'Please enter your invitation code'),
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
border: Border.all(
|
||||
color: state is ActivationError
|
||||
? ColorsManager.red // Red border for error
|
||||
: ColorsManager
|
||||
.grayBox, // Default border color
|
||||
width: 1.5, // Border width
|
||||
),
|
||||
borderRadius:
|
||||
BorderRadius.circular(20.0), // Border radius
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 5,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: TextFormField(
|
||||
validator: (value) {},
|
||||
controller: textEditingController,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Invitation code',
|
||||
hintStyle: context.bodyMedium.copyWith(
|
||||
color: Colors.grey,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
if (textEditingController.text.isEmpty) {
|
||||
CustomSnackBar.displaySnackBar(
|
||||
'Please enter the invitation code');
|
||||
return;
|
||||
}
|
||||
if (await HomeCubit.getInstance()
|
||||
.activationCode(
|
||||
textEditingController.text)) {
|
||||
CustomSnackBar.displaySnackBar(
|
||||
'Done successfully');
|
||||
Navigator.of(context).pop();
|
||||
} else {
|
||||
CustomSnackBar.displaySnackBar(
|
||||
'Wrong code!');
|
||||
}
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.arrow_right_alt,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
state is ActivationError
|
||||
? Text(
|
||||
state.errMessage,
|
||||
style: const TextStyle(
|
||||
color: ColorsManager.red,
|
||||
fontWeight: FontWeight.w400),
|
||||
)
|
||||
: const SizedBox()
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
// return state is! GetSpacesLoading
|
||||
// ? state is! GetSpaceRoomsLoading
|
||||
// ? HomeCubit.getInstance().pages[HomeCubit.pageIndex]
|
||||
// : const Center(child: CircularProgressIndicator())
|
||||
// : const Center(child: CircularProgressIndicator());
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// return SizedBox(
|
||||
// width: MediaQuery.sizeOf(context).width,
|
||||
// height: MediaQuery.sizeOf(context).height,
|
||||
@ -47,5 +173,5 @@ class CreateUnitWidget extends StatelessWidget {
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
}
|
||||
// }
|
||||
// }
|
||||
|
@ -1125,4 +1125,18 @@ class Assets {
|
||||
"assets/icons/edit_device_name_icon.svg";
|
||||
static const String sosHomeIcon = "assets/icons/sos_home_icon.svg";
|
||||
static const String editNameSetting = "assets/icons/edit_name_setting.svg";
|
||||
|
||||
static const String verificationIcon = "assets/icons/verification_icon.svg";
|
||||
|
||||
static const String passwordUnvisibility =
|
||||
"assets/icons/password_unvisibility.svg";
|
||||
static const String passwordVisibility =
|
||||
"assets/icons/password_visibility.svg";
|
||||
|
||||
static const String bathroom = 'assets/icons/bathroom.svg';
|
||||
static const String bedroom = 'assets/icons/bedroom.svg';
|
||||
static const String dyi = 'assets/icons/dyi.svg';
|
||||
static const String office = 'assets/icons/office.svg';
|
||||
static const String parlour = 'assets/icons/parlour.svg';
|
||||
static const String grid = 'assets/images/grid.svg';
|
||||
}
|
||||
|
@ -63,6 +63,8 @@ abstract class ApiEndpoints {
|
||||
static const String unitUser = '/unit/user/';
|
||||
static const String invitationCode =
|
||||
'/projects/{projectUuid}/communities/{communityUuid}/spaces/{unitUuid}/invitation-code';
|
||||
static const String activationCode =
|
||||
'/invite-user/activation';
|
||||
|
||||
//PUT
|
||||
static const String renameUnit = '/unit/{unitUuid}';
|
||||
|
@ -41,7 +41,6 @@ class AuthenticationAPI {
|
||||
body: body,
|
||||
showServerMessage: false,
|
||||
expectedResponseModel: (json) {
|
||||
print(json['data']);
|
||||
return json['data'];
|
||||
});
|
||||
return response;
|
||||
|
@ -448,14 +448,14 @@ class DevicesAPI {
|
||||
required String startTime,
|
||||
required String endTime,
|
||||
}) async {
|
||||
final requestUrl = ApiEndpoints.reportLogs
|
||||
.replaceAll('{deviceUuid}', deviceUuid)
|
||||
.replaceAll('{code}', code)
|
||||
.replaceAll('{startTime}', startTime)
|
||||
.replaceAll('{endTime}', endTime);
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.reportLogs
|
||||
.replaceAll('{deviceUuid}', deviceUuid)
|
||||
.replaceAll('{code}', code)
|
||||
.replaceAll('{startTime}', startTime)
|
||||
.replaceAll('{endTime}', endTime),
|
||||
path: requestUrl,
|
||||
expectedResponseModel: (json) {
|
||||
log('json=====$json');
|
||||
return DeviceReport.fromJson(json);
|
||||
},
|
||||
);
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart';
|
||||
|
@ -31,8 +31,10 @@ class ServerFailure extends Failure {
|
||||
{
|
||||
// var document = parser.parse(dioError.response!.data.toString());
|
||||
// var message = document.body!.text;
|
||||
return ServerFailure.fromResponse(dioError.response!.statusCode!,
|
||||
dioError.response?.data['error']['message'] ?? "Something went wrong");
|
||||
return ServerFailure.fromResponse(
|
||||
dioError.response!.statusCode!,
|
||||
dioError.response?.data['error']['message'] ??
|
||||
"Something went wrong");
|
||||
}
|
||||
case DioExceptionType.cancel:
|
||||
return ServerFailure("The request to ApiServer was canceled");
|
||||
@ -54,15 +56,7 @@ class ServerFailure extends Failure {
|
||||
case 403:
|
||||
return ServerFailure(responseMessage);
|
||||
case 400:
|
||||
List<String> errors = [];
|
||||
if (responseMessage is List) {
|
||||
for (var error in responseMessage) {
|
||||
errors.add(error);
|
||||
}
|
||||
} else {
|
||||
return ServerFailure(responseMessage);
|
||||
}
|
||||
return ServerFailure(errors.join('\n'));
|
||||
return ServerFailure(responseMessage);
|
||||
case 404:
|
||||
return ServerFailure("");
|
||||
case 500:
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'package:syncrow_app/features/app_layout/bloc/home_cubit.dart';
|
||||
import 'package:syncrow_app/features/auth/model/user_model.dart';
|
||||
import 'package:syncrow_app/features/menu/model/region_model.dart';
|
||||
@ -9,14 +10,15 @@ import 'package:syncrow_app/services/api/http_service.dart';
|
||||
class ProfileApi {
|
||||
static final HTTPService _httpService = HTTPService();
|
||||
|
||||
static Future<Map<String, dynamic>> saveName({String? firstName, String? lastName,}) async {
|
||||
static Future<Map<String, dynamic>> saveName({
|
||||
String? firstName,
|
||||
String? lastName,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _httpService.put(
|
||||
path: ApiEndpoints.saveName.replaceAll('{userUuid}', HomeCubit.user!.uuid!),
|
||||
body: {
|
||||
"firstName": firstName,
|
||||
"lastName": lastName
|
||||
},
|
||||
path: ApiEndpoints.saveName
|
||||
.replaceAll('{userUuid}', HomeCubit.user!.uuid!),
|
||||
body: {"firstName": firstName, "lastName": lastName},
|
||||
expectedResponseModel: (json) {
|
||||
return json;
|
||||
},
|
||||
@ -27,10 +29,13 @@ class ProfileApi {
|
||||
}
|
||||
}
|
||||
|
||||
static Future saveRegion({String? regionUuid,}) async {
|
||||
static Future saveRegion({
|
||||
String? regionUuid,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _httpService.put(
|
||||
path: ApiEndpoints.saveRegion.replaceAll('{userUuid}', HomeCubit.user!.uuid!),
|
||||
path: ApiEndpoints.saveRegion
|
||||
.replaceAll('{userUuid}', HomeCubit.user!.uuid!),
|
||||
body: {
|
||||
"regionUuid": regionUuid,
|
||||
},
|
||||
@ -44,10 +49,13 @@ class ProfileApi {
|
||||
}
|
||||
}
|
||||
|
||||
static Future saveTimeZone({String? regionUuid,}) async {
|
||||
static Future saveTimeZone({
|
||||
String? regionUuid,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _httpService.put(
|
||||
path: ApiEndpoints.saveTimeZone.replaceAll('{userUuid}', HomeCubit.user!.uuid!),
|
||||
path: ApiEndpoints.saveTimeZone
|
||||
.replaceAll('{userUuid}', HomeCubit.user!.uuid!),
|
||||
body: {
|
||||
"timezoneUuid": regionUuid,
|
||||
},
|
||||
@ -64,10 +72,9 @@ class ProfileApi {
|
||||
static Future<Map<String, dynamic>> saveImage(String image) async {
|
||||
try {
|
||||
final response = await _httpService.put(
|
||||
path: ApiEndpoints.sendPicture.replaceAll('{userUuid}', HomeCubit.user!.uuid!),
|
||||
body: {
|
||||
"profilePicture": 'data:image/png;base64,$image'
|
||||
},
|
||||
path: ApiEndpoints.sendPicture
|
||||
.replaceAll('{userUuid}', HomeCubit.user!.uuid!),
|
||||
body: {"profilePicture": 'data:image/png;base64,$image'},
|
||||
expectedResponseModel: (json) {
|
||||
return json;
|
||||
},
|
||||
@ -78,14 +85,13 @@ class ProfileApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future fetchUserInfo(userId) async {
|
||||
Future fetchUserInfo(userId) async {
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.getUser.replaceAll('{userUuid}', userId!),
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
return UserModel.fromJson(json);
|
||||
}
|
||||
);
|
||||
path: ApiEndpoints.getUser.replaceAll('{userUuid}', userId!),
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
return UserModel.fromJson(json);
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
@ -94,9 +100,10 @@ class ProfileApi {
|
||||
path: ApiEndpoints.getRegion,
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
return (json as List).map((zone) => RegionModel.fromJson(zone)).toList();
|
||||
}
|
||||
);
|
||||
return (json as List)
|
||||
.map((zone) => RegionModel.fromJson(zone))
|
||||
.toList();
|
||||
});
|
||||
return response as List<RegionModel>;
|
||||
}
|
||||
|
||||
@ -106,9 +113,7 @@ class ProfileApi {
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
return (json as List).map((zone) => TimeZone.fromJson(zone)).toList();
|
||||
}
|
||||
);
|
||||
});
|
||||
return response as List<TimeZone>;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:syncrow_app/features/app_layout/model/space_model.dart';
|
||||
import 'package:syncrow_app/features/auth/model/user_model.dart';
|
||||
@ -66,7 +67,7 @@ class SpacesAPI {
|
||||
|
||||
static Future<String> generateInvitationCode(
|
||||
String unitId, String communityId) async {
|
||||
final response = await _httpService.get(
|
||||
final response = await _httpService.post(
|
||||
path: ApiEndpoints.invitationCode
|
||||
.replaceAll('{unitUuid}', unitId)
|
||||
.replaceAll('{communityUuid}', communityId)
|
||||
@ -95,4 +96,20 @@ class SpacesAPI {
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
static Future activationCodeSpace({
|
||||
String? activationCode,
|
||||
String? userUuid,
|
||||
}) async {
|
||||
Map body = {"activationCode": activationCode, "userUuid": userUuid};
|
||||
final response = await _httpService.post(
|
||||
path: ApiEndpoints.activationCode,
|
||||
showServerMessage: true,
|
||||
body: body,
|
||||
expectedResponseModel: (json) {
|
||||
return json;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
@ -34,5 +34,8 @@ abstract class ColorsManager {
|
||||
static const Color blueColor = Color(0xff5481F3);
|
||||
static const Color blueColor1 = Color(0xff0A7AFF);
|
||||
static const Color grayButtonColors = Color(0xffCCCCCC);
|
||||
static const Color blueButton = Color(0xff85BDFF);
|
||||
|
||||
}
|
||||
//background: #F5F5F5;023DFE
|
||||
//background: #F5F5F5;background: #85BDFF;
|
||||
|
||||
|
@ -5,7 +5,7 @@ import 'package:syncrow_app/features/devices/model/function_model.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/join_home/join_home_view.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/manage_home/manage_home_view.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/privacy/privacy_view.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/securty_view.dart';
|
||||
import 'package:syncrow_app/features/menu/view/widgets/securty/view/securty_view.dart';
|
||||
import 'package:syncrow_app/generated/assets.dart';
|
||||
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
|
||||
|
||||
|
Reference in New Issue
Block a user