From 2ff34a07a72b034c0cd8ed489b54b4c1c60a379d Mon Sep 17 00:00:00 2001 From: Rafeek-Khoudare Date: Tue, 8 Jul 2025 16:21:37 +0300 Subject: [PATCH] add delete account UI and logic --- assets/icons/delete_account_icon.svg | 3 + .../widgets/securty/bloc/security_bloc.dart | 17 +++- .../widgets/securty/bloc/security_event.dart | 2 + .../delete_account/delete_account_page.dart | 88 +++++++++++++++++++ .../securty/view/change_password_page.dart | 5 +- .../widgets/securty/view/securty_view.dart | 70 +++++++++------ .../securty/view/verification_code_page.dart | 73 ++++++++++----- lib/generated/assets.dart | 2 +- lib/services/api/api_links_endpoints.dart | 1 + lib/services/api/authentication_api.dart | 7 ++ 10 files changed, 216 insertions(+), 52 deletions(-) create mode 100644 assets/icons/delete_account_icon.svg create mode 100644 lib/features/menu/view/widgets/securty/delete_account/delete_account_page.dart 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..43b782e 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,18 @@ class SecurityBloc extends Bloc { emit(AuthInitialState()); } } - + + Future onDeleteAccountEvent( + DeleteAccountEvent event, Emitter emit) async { + try { + await AuthenticationAPI.deleteAccount(); + emit(ChangedPassState()); + } on DioException catch (e) { + final errorData = e.response!.data; + String errorMessage = + errorData['error']['message'] ?? 'something went wrong'; + validate = errorMessage; + 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..9ab14a6 --- /dev/null +++ b/lib/features/menu/view/widgets/securty/delete_account/delete_account_page.dart @@ -0,0 +1,88 @@ +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/text_widgets/body_medium.dart'; + +import '../../../../../../generated/assets.dart'; +import '../../../../../../utils/resource_manager/color_manager.dart'; +import '../../../../../shared_widgets/default_scaffold.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: [ + InkWell( + onTap: () { + context.read().add(DeleteAccountEvent()); + }, + 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( + '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",', + fontWeight: FontWeight.w400, + fontSize: 16, + ), + const SizedBox( + height: 4, + ), + const BodyMedium( + text: 'the account will be deleted', + fontWeight: FontWeight.w400, + fontSize: 16, + ), + ], + ), + ), + ); + } +} 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..b8b7d1b 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 @@ -30,7 +30,10 @@ class ChangePasswordPage extends StatelessWidget { MaterialPageRoute( builder: (context) => BlocProvider( create: (_) => SecurityBloc(), // Provide the Bloc - child: const VerificationCodePage(), + 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..d7db084 100644 --- a/lib/features/menu/view/widgets/securty/view/securty_view.dart +++ b/lib/features/menu/view/widgets/securty/view/securty_view.dart @@ -1,10 +1,15 @@ 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/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'; import 'package:syncrow_app/utils/resource_manager/color_manager.dart'; +import '../bloc/security_event.dart'; + class SecurtyView extends StatelessWidget { const SecurtyView({super.key}); @@ -108,33 +113,44 @@ class SecurtyView extends StatelessWidget { // ], // ), // ), - // 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, - // ), - // ], - // ), - // ), + Container( + margin: const EdgeInsets.symmetric(vertical: 15), + height: 1, + color: ColorsManager.greyColor, + ), + InkWell( + onTap: () { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => BlocProvider( + create: (_) => SecurityBloc()..add(StartTimerEvent()), + child: VerificationCodePage( + title: 'Delete Account', + isDeleteAccountMode: true, + ), + ), + )); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + BodyMedium( + text: 'Delete Account', + fontColor: ColorsManager.red, + ), + Icon( + Icons.arrow_forward_ios, + color: ColorsManager.greyColor, + size: 15, + ) + ], + ), + ], + ), + ), // InkWell( // onTap: () {}, // child: const Row( 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..f5f597f 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), @@ -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..50db8ec 100644 --- a/lib/services/api/authentication_api.dart +++ b/lib/services/api/authentication_api.dart @@ -83,4 +83,11 @@ class AuthenticationAPI { ); return response; } + + static Future deleteAccount() async { + await HTTPService().delete( + path: ApiEndpoints.deleteProfile, + expectedResponseModel: (p0) {}, + ); + } }