diff --git a/assets/icons/delete_account_icon.svg b/assets/icons/delete_account_icon.svg new file mode 100644 index 0000000..802417a --- /dev/null +++ b/assets/icons/delete_account_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/features/menu/view/widgets/securty/bloc/security_bloc.dart b/lib/features/menu/view/widgets/securty/bloc/security_bloc.dart index cfd4b6d..891d935 100644 --- a/lib/features/menu/view/widgets/securty/bloc/security_bloc.dart +++ b/lib/features/menu/view/widgets/securty/bloc/security_bloc.dart @@ -20,6 +20,7 @@ class SecurityBloc extends Bloc { on(_onUpdateTimer); on(verifyCode); on(changePassword); + on(onDeleteAccountEvent); } void _onSetPassword(SetPassword event, Emitter emit) { @@ -180,7 +181,6 @@ class SecurityBloc extends Bloc { ChangePasswordEvent event, Emitter emit) async { emit(LoadingForgetState()); try { - final response = await AuthenticationAPI.forgetPassword( email: HomeCubit.user!.email!, otpCode: event.otpCode, @@ -195,5 +195,16 @@ class SecurityBloc extends Bloc { emit(AuthInitialState()); } } - + + Future onDeleteAccountEvent( + DeleteAccountEvent event, Emitter emit) async { + emit(LoadingForgetState()); + try { + await AuthenticationAPI.deleteAccount(); + emit(ChangedPassState()); + } catch (e) { + validate = e.toString(); + emit(AuthInitialState()); + } + } } diff --git a/lib/features/menu/view/widgets/securty/bloc/security_event.dart b/lib/features/menu/view/widgets/securty/bloc/security_event.dart index 352192c..c5b55bf 100644 --- a/lib/features/menu/view/widgets/securty/bloc/security_event.dart +++ b/lib/features/menu/view/widgets/securty/bloc/security_event.dart @@ -29,3 +29,5 @@ class ChangePasswordEvent extends SecurityEvent { } class VerifyPassCodeEvent extends SecurityEvent {} + +class DeleteAccountEvent extends SecurityEvent {} diff --git a/lib/features/menu/view/widgets/securty/delete_account/delete_account_page.dart b/lib/features/menu/view/widgets/securty/delete_account/delete_account_page.dart new file mode 100644 index 0000000..117ad2c --- /dev/null +++ b/lib/features/menu/view/widgets/securty/delete_account/delete_account_page.dart @@ -0,0 +1,107 @@ +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_medium.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; + +class DeleteAccountPage extends StatelessWidget { + const DeleteAccountPage({super.key}); + + @override + Widget build(BuildContext context) { + return BlocListener( + listener: (context, state) { + if (state is ChangedPassState) { + AuthCubit.get(context).logout(); + } + }, + child: DefaultScaffold( + title: 'Delete Account', + bottomNavBar: SizedBox( + height: 150, + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + children: [ + BodyMedium( + text: 'Thank you for using Syncrow', + fontWeight: FontWeight.w400, + fontSize: 14, + textAlign: TextAlign.center, + ), + SizedBox( + height: 10, + ), + BlocBuilder( + builder: (context, state) { + if (state is LoadingForgetState) { + return Center( + child: CircularProgressIndicator(), + ); + } + return ElevatedButton( + onPressed: () { + context + .read() + .add(DeleteAccountEvent()); + }, + style: ElevatedButton.styleFrom( + backgroundColor: ColorsManager.blueColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + fixedSize: Size( + MediaQuery.sizeOf(context).width * 0.8, + 40, + ), + ), + child: Text( + 'Delete Account', + 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.deleteAccountIcon), + ), + const Padding( + padding: EdgeInsets.all(8.0), + child: BodyMedium( + text: 'Account Verification', + fontWeight: FontWeight.w700, + fontSize: 18, + ), + ), + const BodyMedium( + text: + 'if you confirm to "delete account",the account will be deleted', + fontWeight: FontWeight.w400, + fontSize: 16, + textAlign: TextAlign.center, + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/menu/view/widgets/securty/view/change_password_page.dart b/lib/features/menu/view/widgets/securty/view/change_password_page.dart index 75ef741..e09514b 100644 --- a/lib/features/menu/view/widgets/securty/view/change_password_page.dart +++ b/lib/features/menu/view/widgets/securty/view/change_password_page.dart @@ -1,5 +1,4 @@ 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'; @@ -24,13 +23,15 @@ class ChangePasswordPage extends StatelessWidget { children: [ InkWell( onTap: () { - // In your parent widget or navigator Navigator.push( context, MaterialPageRoute( builder: (context) => BlocProvider( - create: (_) => SecurityBloc(), // Provide the Bloc - child: const VerificationCodePage(), + create: (_) => SecurityBloc(), + child: const VerificationCodePage( + title: 'Change Password', + isDeleteAccountMode: false, + ), ), ), ); diff --git a/lib/features/menu/view/widgets/securty/view/securty_view.dart b/lib/features/menu/view/widgets/securty/view/securty_view.dart index abfb7f8..837ea72 100644 --- a/lib/features/menu/view/widgets/securty/view/securty_view.dart +++ b/lib/features/menu/view/widgets/securty/view/securty_view.dart @@ -1,5 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.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/view/change_password_page.dart'; +import 'package:syncrow_app/features/menu/view/widgets/securty/view/verification_code_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,134 +27,34 @@ class SecurtyView extends StatelessWidget { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, children: [ - InkWell( + SecurityListTileWidget( + title: 'Change Password', onTap: () { Navigator.of(context).push(MaterialPageRoute( builder: (context) => const ChangePasswordPage(), )); }, - child: const Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - BodyMedium( - text: 'Change Password', - ), - Icon( - Icons.arrow_forward_ios, - color: ColorsManager.greyColor, - size: 15, - ) - ], - ), - // Container( - // margin: const EdgeInsets.symmetric(vertical: 15), - // height: 1, - // color: ColorsManager.greyColor, - // ), - ], - ), ), - // InkWell( - // onTap: () {}, - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // mainAxisSize: MainAxisSize.min, - // children: [ - // const Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // BodyMedium( - // text: 'App Lock', - // ), - // Icon( - // Icons.arrow_forward_ios, - // color: ColorsManager.greyColor, - // size: 15, - // ) - // ], - // ), - // Container( - // margin: const EdgeInsets.symmetric(vertical: 15), - // height: 1, - // color: ColorsManager.greyColor, - // ), - // ], - // ), - // ), - // InkWell( - // onTap: () {}, - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // mainAxisSize: MainAxisSize.min, - // children: [ - // const Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // BodyMedium( - // text: 'User Code', - // ), - // Icon( - // Icons.arrow_forward_ios, - // color: ColorsManager.greyColor, - // size: 15, - // ) - // ], - // ), - // Container( - // margin: const EdgeInsets.symmetric(vertical: 15), - // height: 1, - // color: ColorsManager.greyColor, - // ), - // ], - // ), - // ), - // InkWell( - // onTap: () {}, - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // mainAxisSize: MainAxisSize.min, - // children: [ - // const Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // BodyMedium( - // text: 'Delete Account', - // ), - // Icon( - // Icons.arrow_forward_ios, - // color: ColorsManager.greyColor, - // size: 15, - // ) - // ], - // ), - // Container( - // margin: const EdgeInsets.symmetric(vertical: 15), - // height: 1, - // color: ColorsManager.greyColor, - // ), - // ], - // ), - // ), - // InkWell( - // onTap: () {}, - // child: const Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // BodyMedium( - // text: 'Device Update', - // ), - // Icon( - // Icons.arrow_forward_ios, - // color: ColorsManager.greyColor, - // size: 15, - // ) - // ], - // ), - // ), + Container( + margin: const EdgeInsets.symmetric(vertical: 15), + height: 1, + color: ColorsManager.greyColor, + ), + SecurityListTileWidget( + title: 'Delete Account', + fontColor: ColorsManager.red, + onTap: () { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => BlocProvider( + create: (_) => SecurityBloc()..add(StartTimerEvent()), + child: VerificationCodePage( + title: '', + isDeleteAccountMode: true, + ), + ), + )); + }, + ), ], ), ), @@ -159,3 +63,42 @@ class SecurtyView extends StatelessWidget { ); } } + +class SecurityListTileWidget extends StatelessWidget { + final String title; + final void Function() onTap; + final Color? fontColor; + const SecurityListTileWidget({ + super.key, + required this.title, + required this.onTap, + this.fontColor, + }); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + BodyMedium( + text: title, + fontColor: fontColor, + ), + Icon( + Icons.arrow_forward_ios, + color: ColorsManager.greyColor, + size: 15, + ) + ], + ), + ], + ), + ); + } +} diff --git a/lib/features/menu/view/widgets/securty/view/verification_code_page.dart b/lib/features/menu/view/widgets/securty/view/verification_code_page.dart index be47e37..849444f 100644 --- a/lib/features/menu/view/widgets/securty/view/verification_code_page.dart +++ b/lib/features/menu/view/widgets/securty/view/verification_code_page.dart @@ -4,6 +4,7 @@ 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/delete_account/delete_account_page.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'; @@ -11,7 +12,13 @@ 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}); + final String title; + final bool isDeleteAccountMode; + const VerificationCodePage({ + super.key, + required this.title, + required this.isDeleteAccountMode, + }); @override Widget build(BuildContext context) { @@ -20,19 +27,29 @@ class VerificationCodePage extends StatelessWidget { create: (context) => SecurityBloc()..add(StartTimerEvent()), child: BlocConsumer( listener: (context, state) { + final securityBloc = context.read(); if (state is SuccessForgetState) { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => SetPasswordPage( - otpCode: otp, - ), - )); + if (isDeleteAccountMode) { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => BlocProvider.value( + value: securityBloc, + child: DeleteAccountPage(), + ), + )); + } else { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => SetPasswordPage( + otpCode: otp, + ), + )); + } } }, builder: (context, state) { final _bloc = BlocProvider.of(context); return DefaultScaffold( - title: 'Change Password', + title: title, child: Column( children: [ const SizedBox(height: 55), @@ -86,8 +103,8 @@ class VerificationCodePage extends StatelessWidget { selectedFillColor: Colors.white70, activeFillColor: Colors.white, errorBorderColor: Colors.white, - fieldHeight: 55.0, - fieldWidth: 55.0, + fieldHeight: 45.0, + fieldWidth: 45.0, fieldOuterPadding: const EdgeInsets.only(right: 8), borderRadius: BorderRadius.circular(17), borderWidth: 1, @@ -109,27 +126,35 @@ class VerificationCodePage extends StatelessWidget { children: [ Expanded( child: InkWell( - onTap: - state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 - ? null - : () { - _bloc.add(StartTimerEvent()); - }, + 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), + 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))), + 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"}', + 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 + color: state is TimerState && + !state.isButtonEnabled ? Colors.white : ColorsManager.onPrimaryColor, ), @@ -142,18 +167,24 @@ class VerificationCodePage extends StatelessWidget { Expanded( child: InkWell( onTap: () { - context.read().add(VerifyPassCodeEvent()); + context + .read() + .add(VerifyPassCodeEvent()); }, child: Container( - padding: const EdgeInsets.only(right: 20, left: 20, top: 15, bottom: 15), + padding: const EdgeInsets.only( + right: 20, left: 20, top: 15, bottom: 15), decoration: const BoxDecoration( color: ColorsManager.blueColor, - borderRadius: BorderRadius.all(Radius.circular(20))), + borderRadius: + BorderRadius.all(Radius.circular(20))), child: const Center( child: Text( "Verify", style: TextStyle( - fontSize: 16, color: Colors.white, fontWeight: FontWeight.w700), + fontSize: 16, + color: Colors.white, + fontWeight: FontWeight.w700), ), ), ), diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index 861444c..614ebe6 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -1128,7 +1128,7 @@ class Assets { static const String editNameSetting = "assets/icons/edit_name_setting.svg"; static const String verificationIcon = "assets/icons/verification_icon.svg"; - +static const String deleteAccountIcon='assets/icons/delete_account_icon.svg'; static const String passwordUnvisibility = "assets/icons/password_unvisibility.svg"; static const String passwordVisibility = diff --git a/lib/services/api/api_links_endpoints.dart b/lib/services/api/api_links_endpoints.dart index e66849b..5d02916 100644 --- a/lib/services/api/api_links_endpoints.dart +++ b/lib/services/api/api_links_endpoints.dart @@ -195,6 +195,7 @@ abstract class ApiEndpoints { static const String sendPicture = '/user/profile-picture/{userUuid}'; static const String getRegion = '/region'; static const String getTimezone = '/timezone'; + static const String deleteProfile = '/user'; //multiple-time offline static const String addMultipleTimeTemporaryPassword = diff --git a/lib/services/api/authentication_api.dart b/lib/services/api/authentication_api.dart index 6579e4d..e813601 100644 --- a/lib/services/api/authentication_api.dart +++ b/lib/services/api/authentication_api.dart @@ -1,3 +1,4 @@ +import 'package:dio/dio.dart'; import 'package:syncrow_app/features/auth/model/login_with_email_model.dart'; import 'package:syncrow_app/features/auth/model/signup_model.dart'; import 'package:syncrow_app/features/auth/model/token.dart'; @@ -83,4 +84,18 @@ class AuthenticationAPI { ); return response; } + + static Future deleteAccount() async { + try { + await HTTPService().delete( + path: ApiEndpoints.deleteProfile, + expectedResponseModel: (p0) {}, + ); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = + errorData['error']['message'] ?? 'something went wrong'; + throw Exception(errorMessage); + } + } }