Bug fixes, and added a logout button

This commit is contained in:
Abdullah Alassaf
2024-09-05 15:22:14 +03:00
parent b565b646c1
commit b94717bc70
4 changed files with 139 additions and 127 deletions

View File

@ -31,8 +31,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
////////////////////////////// forget password ////////////////////////////////// ////////////////////////////// forget password //////////////////////////////////
final TextEditingController forgetEmailController = TextEditingController(); final TextEditingController forgetEmailController = TextEditingController();
final TextEditingController forgetPasswordController = final TextEditingController forgetPasswordController = TextEditingController();
TextEditingController();
final TextEditingController forgetOtp = TextEditingController(); final TextEditingController forgetOtp = TextEditingController();
final forgetFormKey = GlobalKey<FormState>(); final forgetFormKey = GlobalKey<FormState>();
late bool checkValidate = false; late bool checkValidate = false;
@ -41,15 +40,13 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
int _remainingTime = 0; int _remainingTime = 0;
List<RegionModel>? regionList = [RegionModel(name: 'name', id: 'id')]; List<RegionModel>? regionList = [RegionModel(name: 'name', id: 'id')];
Future<void> _onStartTimer( Future<void> _onStartTimer(StartTimerEvent event, Emitter<AuthState> emit) async {
StartTimerEvent event, Emitter<AuthState> emit) async {
if (_validateInputs(emit)) return; if (_validateInputs(emit)) return;
if (_timer != null && _timer!.isActive) { if (_timer != null && _timer!.isActive) {
return; return;
} }
_remainingTime = 1; _remainingTime = 1;
add(UpdateTimerEvent( add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
remainingTime: _remainingTime, isButtonEnabled: false));
_remainingTime = (await AuthenticationAPI.sendOtp( _remainingTime = (await AuthenticationAPI.sendOtp(
email: forgetEmailController.text, regionUuid: regionUuid))!; email: forgetEmailController.text, regionUuid: regionUuid))!;
_timer = Timer.periodic(const Duration(seconds: 1), (timer) { _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
@ -58,8 +55,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
_timer?.cancel(); _timer?.cancel();
add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true)); add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true));
} else { } else {
add(UpdateTimerEvent( add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
remainingTime: _remainingTime, isButtonEnabled: false));
} }
}); });
} }
@ -69,36 +65,32 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
emit(const TimerState(isButtonEnabled: true, remainingTime: 0)); emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
} }
Future<void> changePassword( Future<void> changePassword(ChangePasswordEvent event, Emitter<AuthState> emit) async {
ChangePasswordEvent event, Emitter<AuthState> emit) async {
try { try {
emit(LoadingForgetState()); emit(LoadingForgetState());
var response = await AuthenticationAPI.verifyOtp( var response = await AuthenticationAPI.verifyOtp(
email: forgetEmailController.text, otpCode: forgetOtp.text); email: forgetEmailController.text, otpCode: forgetOtp.text);
if (response == true) { if (response == true) {
await AuthenticationAPI.forgetPassword( await AuthenticationAPI.forgetPassword(
password: forgetPasswordController.text, password: forgetPasswordController.text, email: forgetEmailController.text);
email: forgetEmailController.text);
_timer?.cancel(); _timer?.cancel();
emit(const TimerState(isButtonEnabled: true, remainingTime: 0)); emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
emit(SuccessForgetState()); emit(SuccessForgetState());
} }
} on DioException catch (e) {
final errorData = e.response!.data;
String errorMessage = errorData['message'];
if (errorMessage == 'this email is not registered') {
validate = 'Invalid Credentials!';
emit(AuthInitialState());
} else if (errorMessage == "You entered wrong otp") {
forgetValidate = 'Wrong one time password.';
emit(AuthInitialState());
} else if (errorMessage == "OTP expired") {
forgetValidate = 'One time password has been expired.';
emit(AuthInitialState());
}
} }
on DioException catch (e) {
final errorData = e.response!.data;
String errorMessage = errorData['message'];
if(errorMessage=='this email is not registered'){
validate='Invalid Credentials!';
emit(AuthInitialState());
}else if (errorMessage == "You entered wrong otp") {
forgetValidate = 'Wrong one time password.';
emit(AuthInitialState());
} else if (errorMessage == "OTP expired") {
forgetValidate = 'One time password has been expired.';
emit(AuthInitialState());
}
}
} }
String? validateCode(String? value) { String? validateCode(String? value) {
@ -109,9 +101,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
} }
void _onUpdateTimer(UpdateTimerEvent event, Emitter<AuthState> emit) { void _onUpdateTimer(UpdateTimerEvent event, Emitter<AuthState> emit) {
emit(TimerState( emit(TimerState(isButtonEnabled: event.isButtonEnabled, remainingTime: event.remainingTime));
isButtonEnabled: event.isButtonEnabled,
remainingTime: event.remainingTime));
} }
///////////////////////////////////// login ///////////////////////////////////// ///////////////////////////////////// login /////////////////////////////////////
@ -142,9 +132,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
token = await AuthenticationAPI.loginWithEmail( token = await AuthenticationAPI.loginWithEmail(
model: LoginWithEmailModel( model: LoginWithEmailModel(
email: event.username, email: event.username, password: event.password, regionUuid: event.regionUuid),
password: event.password,
regionUuid: event.regionUuid),
); );
} catch (failure) { } catch (failure) {
validate = 'Invalid Credentials!'; validate = 'Invalid Credentials!';
@ -154,8 +142,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
if (token.accessTokenIsNotEmpty) { if (token.accessTokenIsNotEmpty) {
FlutterSecureStorage storage = const FlutterSecureStorage(); FlutterSecureStorage storage = const FlutterSecureStorage();
await storage.write( await storage.write(key: Token.loginAccessTokenKey, value: token.accessToken);
key: Token.loginAccessTokenKey, value: token.accessToken);
const FlutterSecureStorage().write( const FlutterSecureStorage().write(
key: UserModel.userUuidKey, key: UserModel.userUuidKey,
value: Token.decodeToken(token.accessToken)['uuid'].toString()); value: Token.decodeToken(token.accessToken)['uuid'].toString());
@ -310,14 +297,12 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
static Future<String> getTokenAndValidate() async { static Future<String> getTokenAndValidate() async {
try { try {
const storage = FlutterSecureStorage(); const storage = FlutterSecureStorage();
final firstLaunch = await SharedPreferencesHelper.readBoolFromSP( final firstLaunch =
StringsManager.firstLaunch) ?? await SharedPreferencesHelper.readBoolFromSP(StringsManager.firstLaunch) ?? true;
true;
if (firstLaunch) { if (firstLaunch) {
storage.deleteAll(); storage.deleteAll();
} }
await SharedPreferencesHelper.saveBoolToSP( await SharedPreferencesHelper.saveBoolToSP(StringsManager.firstLaunch, false);
StringsManager.firstLaunch, false);
final value = await storage.read(key: Token.loginAccessTokenKey) ?? ''; final value = await storage.read(key: Token.loginAccessTokenKey) ?? '';
if (value.isEmpty) { if (value.isEmpty) {
return 'Token not found'; return 'Token not found';
@ -369,9 +354,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
final String formattedTime = [ final String formattedTime = [
if (days > 0) '${days}d', // Append 'd' for days if (days > 0) '${days}d', // Append 'd' for days
if (days > 0 || hours > 0) if (days > 0 || hours > 0)
hours hours.toString().padLeft(2, '0'), // Show hours if there are days or hours
.toString()
.padLeft(2, '0'), // Show hours if there are days or hours
minutes.toString().padLeft(2, '0'), minutes.toString().padLeft(2, '0'),
seconds.toString().padLeft(2, '0'), seconds.toString().padLeft(2, '0'),
].join(':'); ].join(':');
@ -409,4 +392,9 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
forgetValidate = ''; forgetValidate = '';
emit(LoginInitial()); emit(LoginInitial());
} }
static logout() {
const storage = FlutterSecureStorage();
storage.deleteAll();
}
} }

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/utils/color_manager.dart';
Future<void> showCustomDialog({ Future<void> showCustomDialog({
required BuildContext context, required BuildContext context,
@ -13,7 +12,7 @@ Future<void> showCustomDialog({
double? iconWidth, double? iconWidth,
VoidCallback? onOkPressed, VoidCallback? onOkPressed,
bool barrierDismissible = false, bool barrierDismissible = false,
required actions, required List<Widget> actions,
}) { }) {
return showDialog( return showDialog(
context: context, context: context,
@ -21,59 +20,43 @@ Future<void> showCustomDialog({
builder: (BuildContext context) { builder: (BuildContext context) {
final size = MediaQuery.of(context).size; final size = MediaQuery.of(context).size;
return AlertDialog( return AlertDialog(
alignment: Alignment.center, alignment: Alignment.center,
content: SizedBox( content: SizedBox(
height: dialogHeight ?? size.height * 0.15, height: dialogHeight ?? size.height * 0.15,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
if (iconPath != null) if (iconPath != null)
SvgPicture.asset( SvgPicture.asset(
iconPath, iconPath,
height: iconHeight ?? 35, height: iconHeight ?? 35,
width: iconWidth ?? 35, width: iconWidth ?? 35,
), ),
if (title != null) if (title != null)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
title,
style: Theme.of(context)
.textTheme
.headlineLarge!
.copyWith(fontSize: 20, fontWeight: FontWeight.w400, color: Colors.black),
),
),
Padding( Padding(
padding: const EdgeInsets.only(top: 8.0), padding: const EdgeInsets.only(top: 8.0),
child: Text( child: Text(
title, message,
style: Theme.of(context).textTheme.headlineLarge!.copyWith( style: Theme.of(context).textTheme.bodyMedium!.copyWith(color: Colors.black),
fontSize: 20, textAlign: TextAlign.center,
fontWeight: FontWeight.w400,
color: Colors.black),
), ),
), ),
Padding( if (widget != null) Expanded(child: widget)
padding: const EdgeInsets.only(top: 8.0), ],
child: Text(
message,
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Colors.black),
textAlign: TextAlign.center,
),
),
if(widget!=null)
Expanded(child:widget)
],
),
),
actionsAlignment: MainAxisAlignment.center,
actions: <Widget>[
TextButton(
onPressed: onOkPressed ?? () => Navigator.of(context).pop(),
child: Text(
'OK',
style: Theme.of(context).textTheme.bodySmall!.copyWith(
fontWeight: FontWeight.w400,
color: ColorsManager.blackColor,
fontSize: 16),
), ),
), ),
], actionsAlignment: MainAxisAlignment.center,
); actions: actions);
}, },
); );
} }

View File

@ -6,14 +6,14 @@ import 'package:syncrow_web/services/devices_mang_api.dart';
part 'device_managment_event.dart'; part 'device_managment_event.dart';
part 'device_managment_state.dart'; part 'device_managment_state.dart';
class DeviceManagementBloc class DeviceManagementBloc extends Bloc<DeviceManagementEvent, DeviceManagementState> {
extends Bloc<DeviceManagementEvent, DeviceManagementState> {
int _selectedIndex = 0; int _selectedIndex = 0;
List<AllDevicesModel> _devices = []; List<AllDevicesModel> _devices = [];
int _onlineCount = 0; int _onlineCount = 0;
int _offlineCount = 0; int _offlineCount = 0;
int _lowBatteryCount = 0; int _lowBatteryCount = 0;
List<AllDevicesModel> _selectedDevices = []; List<AllDevicesModel> _selectedDevices = [];
String productName = '';
DeviceManagementBloc() : super(DeviceManagementInitial()) { DeviceManagementBloc() : super(DeviceManagementInitial()) {
on<FetchDevices>(_onFetchDevices); on<FetchDevices>(_onFetchDevices);
@ -23,8 +23,7 @@ class DeviceManagementBloc
on<SelectDevice>(_onSelectDevice); on<SelectDevice>(_onSelectDevice);
} }
Future<void> _onFetchDevices( Future<void> _onFetchDevices(FetchDevices event, Emitter<DeviceManagementState> emit) async {
FetchDevices event, Emitter<DeviceManagementState> emit) async {
emit(DeviceManagementLoading()); emit(DeviceManagementLoading());
try { try {
final devices = await DevicesManagementApi().fetchDevices(); final devices = await DevicesManagementApi().fetchDevices();
@ -44,8 +43,7 @@ class DeviceManagementBloc
} }
} }
void _onFilterDevices( void _onFilterDevices(FilterDevices event, Emitter<DeviceManagementState> emit) async {
FilterDevices event, Emitter<DeviceManagementState> emit) {
if (_devices.isNotEmpty) { if (_devices.isNotEmpty) {
final filteredDevices = _devices.where((device) { final filteredDevices = _devices.where((device) {
switch (event.filter) { switch (event.filter) {
@ -65,20 +63,20 @@ class DeviceManagementBloc
onlineCount: _onlineCount, onlineCount: _onlineCount,
offlineCount: _offlineCount, offlineCount: _offlineCount,
lowBatteryCount: _lowBatteryCount, lowBatteryCount: _lowBatteryCount,
selectedDevice: selectedDevice: _selectedDevices.isNotEmpty ? _selectedDevices.first : null,
_selectedDevices.isNotEmpty ? _selectedDevices.first : null,
)); ));
if (productName.isNotEmpty) {
add(SearchDevices(productName: productName));
}
} }
} }
void _onSelectedFilterChanged( void _onSelectedFilterChanged(SelectedFilterChanged event, Emitter<DeviceManagementState> emit) {
SelectedFilterChanged event, Emitter<DeviceManagementState> emit) {
_selectedIndex = event.selectedIndex; _selectedIndex = event.selectedIndex;
add(FilterDevices(_getFilterFromIndex(_selectedIndex))); add(FilterDevices(_getFilterFromIndex(_selectedIndex)));
} }
void _onSelectDevice( void _onSelectDevice(SelectDevice event, Emitter<DeviceManagementState> emit) {
SelectDevice event, Emitter<DeviceManagementState> emit) {
final selectedUuid = event.selectedDevice.uuid; final selectedUuid = event.selectedDevice.uuid;
if (_selectedDevices.any((device) => device.uuid == selectedUuid)) { if (_selectedDevices.any((device) => device.uuid == selectedUuid)) {
@ -113,10 +111,8 @@ class DeviceManagementBloc
void _calculateDeviceCounts() { void _calculateDeviceCounts() {
_onlineCount = _devices.where((device) => device.online == true).length; _onlineCount = _devices.where((device) => device.online == true).length;
_offlineCount = _devices.where((device) => device.online == false).length; _offlineCount = _devices.where((device) => device.online == false).length;
_lowBatteryCount = _devices _lowBatteryCount =
.where((device) => _devices.where((device) => device.batteryLevel != null && device.batteryLevel! < 20).length;
device.batteryLevel != null && device.batteryLevel! < 20)
.length;
} }
String _getFilterFromIndex(int index) { String _getFilterFromIndex(int index) {
@ -132,18 +128,20 @@ class DeviceManagementBloc
} }
} }
void _onSearchDevices( void _onSearchDevices(SearchDevices event, Emitter<DeviceManagementState> emit) {
SearchDevices event, Emitter<DeviceManagementState> emit) {
// If the search fields are all empty, restore the last filtered devices // If the search fields are all empty, restore the last filtered devices
if ((event.community == null || event.community!.isEmpty) && if ((event.community == null || event.community!.isEmpty) &&
(event.unitName == null || event.unitName!.isEmpty) && (event.unitName == null || event.unitName!.isEmpty) &&
(event.productName == null || event.productName!.isEmpty)) { (event.productName == null || event.productName!.isEmpty)) {
productName = '';
// If the current state is filtered, re-emit the filtered state // If the current state is filtered, re-emit the filtered state
if (state is DeviceManagementFiltered) { if (state is DeviceManagementFiltered) {
add(FilterDevices(_getFilterFromIndex(_selectedIndex))); add(FilterDevices(_getFilterFromIndex(_selectedIndex)));
} }
} }
productName = event.productName ?? '';
List<AllDevicesModel> devicesToSearch = _devices; List<AllDevicesModel> devicesToSearch = _devices;
if (state is DeviceManagementFiltered) { if (state is DeviceManagementFiltered) {
@ -157,32 +155,19 @@ class DeviceManagementBloc
final filteredDevices = devicesToSearch.where((device) { final filteredDevices = devicesToSearch.where((device) {
final matchesCommunity = event.community == null || final matchesCommunity = event.community == null ||
event.community!.isEmpty || event.community!.isEmpty ||
(device.room?.name (device.room?.name?.toLowerCase().contains(event.community!.toLowerCase()) ?? false);
?.toLowerCase()
.contains(event.community!.toLowerCase()) ??
false);
final matchesUnit = event.unitName == null || final matchesUnit = event.unitName == null ||
event.unitName!.isEmpty || event.unitName!.isEmpty ||
(device.unit?.name (device.unit?.name?.toLowerCase().contains(event.unitName!.toLowerCase()) ?? false);
?.toLowerCase()
.contains(event.unitName!.toLowerCase()) ??
false);
final matchesProductName = event.productName == null || final matchesProductName = event.productName == null ||
event.productName!.isEmpty || event.productName!.isEmpty ||
(device.name (device.name?.toLowerCase().contains(event.productName!.toLowerCase()) ?? false);
?.toLowerCase()
.contains(event.productName!.toLowerCase()) ??
false);
final matchesDeviceName = event.productName == null || final matchesDeviceName = event.productName == null ||
event.productName!.isEmpty || event.productName!.isEmpty ||
(device.categoryName (device.categoryName?.toLowerCase().contains(event.productName!.toLowerCase()) ??
?.toLowerCase()
.contains(event.productName!.toLowerCase()) ??
false); false);
return matchesCommunity && return matchesCommunity && matchesUnit && (matchesProductName || matchesDeviceName);
matchesUnit &&
(matchesProductName || matchesDeviceName);
}).toList(); }).toList();
emit(DeviceManagementFiltered( emit(DeviceManagementFiltered(

View File

@ -1,8 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/common/custom_dialog.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_state.dart'; import 'package:syncrow_web/pages/home/bloc/home_state.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class WebAppBar extends StatefulWidget { class WebAppBar extends StatefulWidget {
@ -128,6 +133,57 @@ class _WebAppBarState extends State<WebAppBar> with HelperResponsiveLayout {
'${user.firstName} ${user.lastName}', '${user.firstName} ${user.lastName}',
style: Theme.of(context).textTheme.bodyLarge, style: Theme.of(context).textTheme.bodyLarge,
), ),
const SizedBox(
width: 10,
),
GestureDetector(
onTap: () {
showCustomDialog(
context: context,
barrierDismissible: true,
title: 'Logout',
message: 'Are you sure you want to logout?',
actions: [
GestureDetector(
onTap: () {
AuthBloc.logout();
context.go(RoutesConst.auth);
},
child: DefaultButton(
child: Text(
'Ok',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(fontSize: 12, color: Colors.white),
),
),
),
const SizedBox(
height: 10,
),
GestureDetector(
onTap: () {
context.pop();
},
child: DefaultButton(
child: Text(
'Cancel',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(fontSize: 12, color: Colors.white),
),
),
),
],
);
},
child: const Icon(
Icons.logout,
color: ColorsManager.whiteColors,
),
)
], ],
), ),
], ],