password_changes&_routine_changes

This commit is contained in:
mohammad
2024-10-29 17:21:31 +03:00
parent a1b8c79ce4
commit ef232e9cd5
13 changed files with 373 additions and 139 deletions

View File

@ -237,22 +237,26 @@ class AuthCubit extends Cubit<AuthState> {
}
}
sendOtp() async {
sendOtp({bool? isforget}) async {
try {
emit(AuthLoading());
await AuthenticationAPI.sendOtp(
body: {'email': email, 'type': 'VERIFICATION'});
await AuthenticationAPI.sendOtp(body: {
'email': email,
'type': isforget == true ? 'PASSWORD' : 'VERIFICATION'
});
emit(AuthSignUpSuccess());
} catch (_) {
emit(AuthLoginError(message: 'Something went wrong'));
}
}
Future<bool> reSendOtp() async {
Future<bool> reSendOtp({bool? forget}) async {
try {
emit(AuthLoading());
await AuthenticationAPI.sendOtp(
body: {'email': email, 'type': 'VERIFICATION'});
await AuthenticationAPI.sendOtp(body: {
'email': email,
'type': forget == true ? 'PASSWORD' : 'VERIFICATION'
});
emit(ResendOtpSuccess());
return true;
} catch (_) {
@ -264,8 +268,11 @@ class AuthCubit extends Cubit<AuthState> {
verifyOtp(bool isForgotPass) async {
emit(AuthLoginLoading());
try {
final response = await AuthenticationAPI.verifyPassCode(
body: {'email': email, 'type': 'VERIFICATION', 'otpCode': otpCode});
final response = await AuthenticationAPI.verifyPassCode(body: {
'email': email,
'type': isForgotPass == true ? 'PASSWORD' : 'VERIFICATION',
'otpCode': otpCode
});
if (response['statusCode'] == 200) {
if (!isForgotPass) {
emailController.text = email;
@ -340,7 +347,9 @@ class AuthCubit extends Cubit<AuthState> {
sendToForgetPassword({required String password}) async {
try {
emit(AuthForgetPassLoading());
await AuthenticationAPI.forgetPassword(email: email, password: password);
await AuthenticationAPI.forgetPassword(
email: email, password: password, otpCode: otpCode);
emit(AuthForgetPassSuccess());
} catch (_) {
emit(AuthForgetPassError(message: 'Something went wrong'));

View File

@ -15,13 +15,15 @@ import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
import 'package:syncrow_app/utils/resource_manager/styles_manager.dart';
class checkEmailPage extends StatelessWidget {
const checkEmailPage({super.key});
bool? forget;
checkEmailPage({super.key, this.forget});
@override
Widget build(BuildContext context) {
final formKey = AuthCubit.get(context).checkEmailFormKey;
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarBrightness: Brightness.light, statusBarIconBrightness: Brightness.light));
statusBarBrightness: Brightness.light,
statusBarIconBrightness: Brightness.light));
return BlocConsumer<AuthCubit, AuthState>(
listener: (context, state) {
if (state is AuthError) {
@ -34,8 +36,8 @@ class checkEmailPage extends StatelessWidget {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const OtpView(
isForgetPage: true,
builder: (context) => OtpView(
isForgetPage: forget!,
),
));
}
@ -91,7 +93,9 @@ class checkEmailPage extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: MediaQuery.sizeOf(context).height / 5.5,
height:
MediaQuery.sizeOf(context).height /
5.5,
),
TitleMedium(
text: 'Forgot password?',
@ -113,32 +117,39 @@ class checkEmailPage extends StatelessWidget {
scrollPadding: EdgeInsets.zero,
autocorrect: false,
enableSuggestions: false,
autofillHints: const [AutofillHints.email],
validator: AuthCubit.get(context).emailAddressValidator,
autofillHints: const [
AutofillHints.email
],
validator: AuthCubit.get(context)
.emailAddressValidator,
onTapOutside: (event) {
FocusScope.of(context).unfocus();
},
onChanged: (value) {
AuthCubit.get(context).email = value;
},
decoration: defaultInputDecoration(context,
decoration: defaultInputDecoration(
context,
hint: "Example@email.com"),
),
const SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Expanded(
child: DefaultButton(
isDone: state is AuthLoginSuccess,
isLoading: state is AuthLoading,
customButtonStyle: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
backgroundColor:
MaterialStateProperty.all(
Colors.black.withOpacity(.25),
),
foregroundColor: MaterialStateProperty.all(
foregroundColor:
MaterialStateProperty.all(
Colors.white,
),
),
@ -146,11 +157,16 @@ class checkEmailPage extends StatelessWidget {
'Send Code',
),
onPressed: () {
AuthCubit.get(context).showValidationMessage = true;
if (formKey.currentState!.validate()) {
AuthCubit.get(context)
.showValidationMessage = true;
if (formKey.currentState!
.validate()) {
if ((state is! AuthLoading)) {
AuthCubit.get(context).sendOtp();
FocusScope.of(context).unfocus();
AuthCubit.get(context)
.sendOtp(
isforget: forget);
FocusScope.of(context)
.unfocus();
}
}
},
@ -160,14 +176,17 @@ class checkEmailPage extends StatelessWidget {
),
Padding(
padding: EdgeInsets.only(
top: MediaQuery.sizeOf(context).height / 5.5),
top: MediaQuery.sizeOf(context)
.height /
5.5),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
children: <Widget>[
BodyLarge(
text: "Do you have an account? ",
style:
context.displaySmall.copyWith(color: Colors.white),
style: context.displaySmall
.copyWith(color: Colors.white),
),
TextButton(
onPressed: () {
@ -175,7 +194,8 @@ class checkEmailPage extends StatelessWidget {
},
child: BodyLarge(
text: "Sign in",
style: context.displaySmall.copyWith(
style:
context.displaySmall.copyWith(
color: Colors.black,
fontWeight: FontsManager.bold,
),

View File

@ -12,6 +12,7 @@ import 'package:syncrow_app/utils/resource_manager/constants.dart';
import 'package:syncrow_app/utils/resource_manager/font_manager.dart';
import 'package:syncrow_app/utils/resource_manager/styles_manager.dart';
class CreateNewPasswordPage extends StatelessWidget {
const CreateNewPasswordPage({super.key,});

View File

@ -374,15 +374,20 @@ class _OtpViewState extends State<OtpView> {
return;
}
if ((state is! AuthLoading)) {
bool success = await AuthCubit.get(context)
.reSendOtp();
bool success =
await AuthCubit.get(context)
.reSendOtp(
forget:
widget.isForgetPage);
FocusScope.of(context).unfocus();
if(success){
if (success) {
showDialog(
context: context,
builder: (_) => SuccessDialog(
key: ValueKey('SuccessDialog'),
message: 'New OTP sent!',));
key: ValueKey(
'SuccessDialog'),
message: 'New OTP sent!',
));
}
Future.delayed(Duration(seconds: 2),
() {

View File

@ -10,12 +10,17 @@ class ForgetPassword extends StatelessWidget {
@override
Widget build(BuildContext context) {
bool isforget = true;
return Row(
children: [
const Spacer(),
TextButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => const checkEmailPage(),));
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => checkEmailPage(forget: isforget),
));
},
child: BodyMedium(
text: "Forgot Password?",

View File

@ -83,7 +83,8 @@ class _RoomPageState extends State<RoomPage> {
},
),
)
: const Expanded(
: widget.room.devices!.isNotEmpty
? const Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
@ -98,7 +99,8 @@ class _RoomPageState extends State<RoomPage> {
)),
],
),
),
)
: const SizedBox(),
],
);
}

View File

@ -3,8 +3,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart';
import 'package:syncrow_app/features/scene/view/scene_tasks_view.dart';
import 'package:syncrow_app/features/scene/widgets/alert_dialogs/icons_dialog.dart';
import 'package:syncrow_app/features/scene/widgets/delete_routine_b.dart';
import 'package:syncrow_app/features/scene/widgets/effective_period_setting/effective_period_bottom_sheet.dart';
import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart';
import 'package:syncrow_app/features/shared_widgets/default_container.dart';
@ -18,10 +18,12 @@ class SceneAutoSettings extends StatelessWidget {
@override
Widget build(BuildContext context) {
final sceneSettings = ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>? ?? {};
final sceneSettings =
ModalRoute.of(context)!.settings.arguments as Map<String, dynamic>? ??
{};
final sceneId = sceneSettings['sceneId'] as String? ?? '';
final isAutomation =
context.read<CreateSceneBloc>().sceneType == CreateSceneEnum.deviceStatusChanges;
final isAutomation = context.read<CreateSceneBloc>().sceneType ==
CreateSceneEnum.deviceStatusChanges;
final sceneName = sceneSettings['sceneName'] as String? ?? '';
bool showInDevice = context.read<CreateSceneBloc>().showInDeviceScreen;
String selectedIcon = '';
@ -35,7 +37,8 @@ class SceneAutoSettings extends StatelessWidget {
icon: const Icon(
Icons.arrow_back_ios,
)),
child: BlocBuilder<CreateSceneBloc, CreateSceneState>(builder: (context, state) {
child: BlocBuilder<CreateSceneBloc, CreateSceneState>(
builder: (context, state) {
if (state is AddSceneTask) {
showInDevice = state.showInDevice ?? false;
}
@ -47,7 +50,8 @@ class SceneAutoSettings extends StatelessWidget {
if (!isAutomation)
DefaultContainer(
child: Padding(
padding: const EdgeInsets.only(top: 10, left: 10, right: 10, bottom: 10),
padding: const EdgeInsets.only(
top: 10, left: 10, right: 10, bottom: 10),
child: Column(
children: [
InkWell(
@ -55,20 +59,24 @@ class SceneAutoSettings extends StatelessWidget {
showDialog(
context: context,
builder: (context) {
BlocProvider.of<CreateSceneBloc>(context).add(SceneIconEvent());
BlocProvider.of<CreateSceneBloc>(context)
.add(SceneIconEvent());
return IconsDialog(
widgetList: Container(
height: MediaQuery.sizeOf(context).height * 0.4,
height:
MediaQuery.sizeOf(context).height * 0.4,
width: MediaQuery.sizeOf(context).width,
padding: const EdgeInsets.all(24),
child: BlocBuilder<CreateSceneBloc, CreateSceneState>(
child: BlocBuilder<CreateSceneBloc,
CreateSceneState>(
builder: (context, state) {
if (state is CreateSceneLoading) {
return const Center(
child: SizedBox(
height: 50,
width: 50,
child: CircularProgressIndicator()),
child:
CircularProgressIndicator()),
);
} else if (state is AddSceneTask) {
return GridView.builder(
@ -78,25 +86,35 @@ class SceneAutoSettings extends StatelessWidget {
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
itemCount: state.iconModels?.length ?? 0,
itemCount:
state.iconModels?.length ?? 0,
itemBuilder: (context, index) {
final iconModel = state.iconModels![index];
final iconModel =
state.iconModels![index];
return InkWell(
onTap: () {
BlocProvider.of<CreateSceneBloc>(context).add(
IconSelected(
iconId: iconModel.uuid,
confirmSelection: false));
BlocProvider.of<
CreateSceneBloc>(
context)
.add(IconSelected(
iconId:
iconModel.uuid,
confirmSelection:
false));
selectedIcon = iconModel.uuid;
},
child: ClipOval(
child: Container(
padding: const EdgeInsets.all(1),
padding:
const EdgeInsets.all(1),
decoration: BoxDecoration(
border: Border.all(
color: state.selectedIcon == iconModel.uuid
? ColorsManager.primaryColorWithOpacity
: Colors.transparent,
color: state.selectedIcon ==
iconModel.uuid
? ColorsManager
.primaryColorWithOpacity
: Colors
.transparent,
width: 2,
),
shape: BoxShape.circle,
@ -123,8 +141,10 @@ class SceneAutoSettings extends StatelessWidget {
Navigator.of(context).pop();
},
confirmTab: () {
BlocProvider.of<CreateSceneBloc>(context).add(
IconSelected(iconId: selectedIcon, confirmSelection: true));
BlocProvider.of<CreateSceneBloc>(context)
.add(IconSelected(
iconId: selectedIcon,
confirmSelection: true));
Navigator.of(context).pop();
},
title: 'Icons',
@ -171,7 +191,8 @@ class SceneAutoSettings extends StatelessWidget {
value: showInDevice,
onChanged: (value) {
BlocProvider.of<CreateSceneBloc>(context)
.add(ShowOnDeviceClicked(value: value));
.add(ShowOnDeviceClicked(
value: value));
},
applyTheme: true,
),
@ -219,7 +240,8 @@ class SceneAutoSettings extends StatelessWidget {
visible: isAutomation,
child: SceneListTile(
titleString: "Effective Period",
trailingWidget: const Icon(Icons.arrow_forward_ios_rounded),
trailingWidget:
const Icon(Icons.arrow_forward_ios_rounded),
onPressed: () {
context.customBottomSheet(
child: const EffectPeriodBottomSheetContent(),
@ -236,19 +258,29 @@ class SceneAutoSettings extends StatelessWidget {
),
),
),
Visibility(
],
),
),
const SizedBox(
height: 15,
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
child: Center(
child: Visibility(
visible: sceneName.isNotEmpty,
child: DeleteBottomSheetContent(
child: DeleteRoutineButton(
isAutomation: isAutomation,
sceneId: sceneId,
),
),
const SizedBox(
height: 16,
),
),
],
),
),
],
),
);

View File

@ -7,10 +7,10 @@ import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_event.dart';
import 'package:syncrow_app/features/scene/enum/create_scene_enum.dart';
import 'package:syncrow_app/features/scene/model/scene_settings_route_arguments.dart';
import 'package:syncrow_app/features/scene/widgets/alert_dialogs/delete_routine_dialog.dart';
import 'package:syncrow_app/features/scene/widgets/create_scene_save_button.dart';
import 'package:syncrow_app/features/scene/widgets/if_then_containers/if_container.dart';
import 'package:syncrow_app/features/scene/widgets/if_then_containers/then_container.dart';
import 'package:syncrow_app/features/scene/widgets/scene_list_tile.dart';
import 'package:syncrow_app/features/shared_widgets/default_scaffold.dart';
import 'package:syncrow_app/generated/assets.dart';
import 'package:syncrow_app/navigation/navigate_to_route.dart';
@ -117,49 +117,3 @@ class SceneTasksView extends StatelessWidget {
);
}
}
class DeleteBottomSheetContent extends StatelessWidget {
const DeleteBottomSheetContent(
{super.key, required this.sceneId, required this.isAutomation});
final String sceneId;
final bool isAutomation;
@override
Widget build(BuildContext context) {
return BlocConsumer<CreateSceneBloc, CreateSceneState>(
listener: (context, state) {
if (state is DeleteSceneSuccess) {
if (state.success) {
navigateToRoute(context, Routes.homeRoute);
BlocProvider.of<SceneBloc>(context)
.add(LoadScenes(HomeCubit.getInstance().selectedSpace!.id!));
BlocProvider.of<SceneBloc>(context).add(
LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!));
}
}
},
builder: (context, state) {
return SceneListTile(
onPressed: () {
context.read<CreateSceneBloc>().add(DeleteSceneEvent(
sceneId: sceneId,
unitUuid: HomeCubit.getInstance().selectedSpace!.id!,
));
},
padding: const EdgeInsets.symmetric(horizontal: 8),
titleString: isAutomation
? StringsManager.deleteAutomation
: StringsManager.deleteScene,
leadingWidget: (state is DeleteSceneLoading)
? const SizedBox(
height: 24, width: 24, child: CircularProgressIndicator())
: SvgPicture.asset(
Assets.assetsDeleteIcon,
color: ColorsManager.red,
),
);
},
);
}
}

View File

@ -0,0 +1,114 @@
import 'package:flutter/material.dart';
import 'package:syncrow_app/features/shared_widgets/text_widgets/body_large.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class DeleteRoutineDialog extends StatelessWidget {
final Function()? cancelTab;
final Function()? confirmTab;
const DeleteRoutineDialog({
super.key,
required this.cancelTab,
required this.confirmTab,
});
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const SizedBox(
height: 10,
),
const BodyLarge(
text: 'Delete Routine',
fontWeight: FontWeight.w700,
fontColor: ColorsManager.red,
fontSize: 16,
),
const Padding(
padding: EdgeInsets.only(left: 15, right: 15),
child: Divider(
color: ColorsManager.textGray,
),
),
const Padding(
padding: EdgeInsets.only(left: 20, right: 20, top: 20, bottom: 20),
child: Column(
children: [
Center(child: const Text('Are you sure you want to ')),
Center(child: const Text('delete the routine?'))
],
),
),
Row(
children: [
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
right: BorderSide(
color: ColorsManager.textGray,
width: 0.5,
),
top: BorderSide(
color: ColorsManager.textGray,
width: 1.0,
),
)),
child: SizedBox(
child: InkWell(
onTap: cancelTab,
child: const Padding(
padding: EdgeInsets.all(15),
child: Center(
child: Text(
'Cancel',
style: TextStyle(
color: ColorsManager.textGray,
fontSize: 14,
fontWeight: FontWeight.w400),
),
),
),
),
),
),
),
Expanded(
child: Container(
decoration: const BoxDecoration(
border: Border(
left: BorderSide(
color: ColorsManager.textGray,
width: 0.5,
),
top: BorderSide(
color: ColorsManager.textGray,
width: 1.0,
),
)),
child: InkWell(
onTap: confirmTab,
child: const Padding(
padding: EdgeInsets.all(15),
child: Center(
child: Text(
'Confirm',
style: TextStyle(
color: ColorsManager.red,
fontSize: 14,
fontWeight: FontWeight.w400),
),
),
)),
))
],
)
],
),
);
}
}

View File

@ -0,0 +1,81 @@
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/scene/bloc/create_scene/create_scene_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_bloc.dart';
import 'package:syncrow_app/features/scene/bloc/scene_bloc/scene_event.dart';
import 'package:syncrow_app/features/scene/widgets/alert_dialogs/delete_routine_dialog.dart';
import 'package:syncrow_app/navigation/navigate_to_route.dart';
import 'package:syncrow_app/navigation/routing_constants.dart';
import 'package:syncrow_app/utils/resource_manager/color_manager.dart';
class DeleteRoutineButton extends StatelessWidget {
const DeleteRoutineButton(
{super.key, required this.sceneId, required this.isAutomation});
final String sceneId;
final bool isAutomation;
@override
Widget build(BuildContext context) {
return BlocConsumer<CreateSceneBloc, CreateSceneState>(
listener: (context, state) {
if (state is DeleteSceneSuccess) {
if (state.success) {
navigateToRoute(context, Routes.homeRoute);
BlocProvider.of<SceneBloc>(context)
.add(LoadScenes(HomeCubit.getInstance().selectedSpace!.id!));
BlocProvider.of<SceneBloc>(context).add(
LoadAutomation(HomeCubit.getInstance().selectedSpace!.id!));
}
}
},
builder: (context, state) {
return InkWell(
onTap: () {
showDialog(
context: context,
builder: (context) {
return DeleteRoutineDialog(
cancelTab: () {
Navigator.of(context).pop();
},
confirmTab: () {
context.read<CreateSceneBloc>().add(DeleteSceneEvent(
sceneId: sceneId,
unitUuid:
HomeCubit.getInstance().selectedSpace!.id!,
));
Navigator.of(context).pop();
},
);
},
);
},
child: const Center(
child: Text(
'Remove Routine',
style: TextStyle(color: ColorsManager.red),
))
// : SceneListTile(
// onPressed: () {
// },
// padding: const EdgeInsets.symmetric(horizontal: 8),
// titleString: isAutomation
// ? StringsManager.deleteAutomation
// : StringsManager.deleteScene,
// leadingWidget: (state is DeleteSceneLoading)
// ? const SizedBox(
// height: 24,
// width: 24,
// child: CircularProgressIndicator())
// : SvgPicture.asset(
// Assets.assetsDeleteIcon,
// color: ColorsManager.red,
// ),
);
},
);
}
}

View File

@ -5,7 +5,8 @@ import 'package:syncrow_app/services/api/api_links_endpoints.dart';
import 'package:syncrow_app/services/api/http_service.dart';
class AuthenticationAPI {
static Future<Map<String, dynamic>> verifyPassCode({required Map<String, dynamic> body}) async {
static Future<Map<String, dynamic>> verifyPassCode(
{required Map<String, dynamic> body}) async {
final response = await HTTPService().post(
path: ApiEndpoints.verifyOtp,
body: body,
@ -14,7 +15,8 @@ class AuthenticationAPI {
return response;
}
static Future<Token> loginWithEmail({required LoginWithEmailModel model}) async {
static Future<Token> loginWithEmail(
{required LoginWithEmailModel model}) async {
final response = await HTTPService().post(
path: ApiEndpoints.login,
body: model.toJson(),
@ -32,7 +34,8 @@ class AuthenticationAPI {
return response;
}
static Future<Map<String, dynamic>> sendOtp({required Map<String, dynamic> body}) async {
static Future<Map<String, dynamic>> sendOtp(
{required Map<String, dynamic> body}) async {
final response = await HTTPService().post(
path: ApiEndpoints.sendOtp,
body: body,
@ -41,8 +44,16 @@ class AuthenticationAPI {
return response;
}
static Future<Map<String, dynamic>> forgetPassword({required String email,required String password ,}) async {
Map<String, dynamic> params = {"email": email, "password": password,};
static Future<Map<String, dynamic>> forgetPassword({
required String otpCode,
required String email,
required String password,
}) async {
Map<String, dynamic> params = {
"email": email,
"password": password,
"otpCode": otpCode
};
final response = await HTTPService().post(
path: ApiEndpoints.forgetPassword,
body: params,

View File

@ -39,6 +39,6 @@ class StringsManager {
'Example: when an unusual activity is detected.';
static const String functions = "Functions";
static const String firstLaunch = "firstLaunch";
static const String deleteScene = 'Delete Scene';
static const String deleteScene = 'Remove Routine';
static const String deleteAutomation = 'Delete Automation';
}

View File

@ -5,7 +5,7 @@ description: This is the mobile application project, developed with Flutter for
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: "none" # Remove this line if you wish to publish to pub.dev
version: 1.0.5+34
version: 1.0.5+35
environment:
sdk: ">=3.0.6 <4.0.0"