From 8c66c8f29c24d02117823661971ed7bb6f294b94 Mon Sep 17 00:00:00 2001 From: Niraj Nandish Date: Wed, 21 Jun 2023 14:29:14 +0400 Subject: [PATCH] release(app): v3.5.0+30500 (#1021) * feat: add extra dialog for delete account * chore: update app generated files * release(app): v3.5.0+30500 --- mobile-app/lib/app/app.router.dart | 133 ++++++++++++++++++ mobile-app/lib/enums/dialog_type.dart | 1 + .../delete-account/delete_account_view.dart | 24 ---- .../delete_account_viewmodel.dart | 105 ++++++++------ .../lib/ui/widgets/setup_dialog_ui.dart | 100 +++++++++++++ mobile-app/pubspec.yaml | 2 +- 6 files changed, 294 insertions(+), 71 deletions(-) diff --git a/mobile-app/lib/app/app.router.dart b/mobile-app/lib/app/app.router.dart index 75e597f58..0dfe3faa5 100644 --- a/mobile-app/lib/app/app.router.dart +++ b/mobile-app/lib/app/app.router.dart @@ -321,6 +321,19 @@ class EpisodeViewArguments { String toString() { return '{"key": "$key", "episode": "$episode", "podcast": "$podcast"}'; } + + @override + bool operator ==(covariant EpisodeViewArguments other) { + if (identical(this, other)) return true; + return other.key == key && + other.episode == episode && + other.podcast == podcast; + } + + @override + int get hashCode { + return key.hashCode ^ episode.hashCode ^ podcast.hashCode; + } } class NewsTutorialViewArguments { @@ -340,6 +353,17 @@ class NewsTutorialViewArguments { String toString() { return '{"key": "$key", "refId": "$refId", "title": "$title"}'; } + + @override + bool operator ==(covariant NewsTutorialViewArguments other) { + if (identical(this, other)) return true; + return other.key == key && other.refId == refId && other.title == title; + } + + @override + int get hashCode { + return key.hashCode ^ refId.hashCode ^ title.hashCode; + } } class NewsBookmarkTutorialViewArguments { @@ -356,6 +380,17 @@ class NewsBookmarkTutorialViewArguments { String toString() { return '{"key": "$key", "tutorial": "$tutorial"}'; } + + @override + bool operator ==(covariant NewsBookmarkTutorialViewArguments other) { + if (identical(this, other)) return true; + return other.key == key && other.tutorial == tutorial; + } + + @override + int get hashCode { + return key.hashCode ^ tutorial.hashCode; + } } class NewsFeedViewArguments { @@ -390,6 +425,31 @@ class NewsFeedViewArguments { String toString() { return '{"key": "$key", "slug": "$slug", "author": "$author", "fromAuthor": "$fromAuthor", "fromTag": "$fromTag", "fromSearch": "$fromSearch", "tutorials": "$tutorials", "subject": "$subject"}'; } + + @override + bool operator ==(covariant NewsFeedViewArguments other) { + if (identical(this, other)) return true; + return other.key == key && + other.slug == slug && + other.author == author && + other.fromAuthor == fromAuthor && + other.fromTag == fromTag && + other.fromSearch == fromSearch && + other.tutorials == tutorials && + other.subject == subject; + } + + @override + int get hashCode { + return key.hashCode ^ + slug.hashCode ^ + author.hashCode ^ + fromAuthor.hashCode ^ + fromTag.hashCode ^ + fromSearch.hashCode ^ + tutorials.hashCode ^ + subject.hashCode; + } } class NewsAuthorViewArguments { @@ -406,6 +466,17 @@ class NewsAuthorViewArguments { String toString() { return '{"key": "$key", "authorSlug": "$authorSlug"}'; } + + @override + bool operator ==(covariant NewsAuthorViewArguments other) { + if (identical(this, other)) return true; + return other.key == key && other.authorSlug == authorSlug; + } + + @override + int get hashCode { + return key.hashCode ^ authorSlug.hashCode; + } } class NewsImageViewArguments { @@ -425,6 +496,19 @@ class NewsImageViewArguments { String toString() { return '{"key": "$key", "imgUrl": "$imgUrl", "isDataUrl": "$isDataUrl"}'; } + + @override + bool operator ==(covariant NewsImageViewArguments other) { + if (identical(this, other)) return true; + return other.key == key && + other.imgUrl == imgUrl && + other.isDataUrl == isDataUrl; + } + + @override + int get hashCode { + return key.hashCode ^ imgUrl.hashCode ^ isDataUrl.hashCode; + } } class ChallengeViewArguments { @@ -453,6 +537,27 @@ class ChallengeViewArguments { String toString() { return '{"key": "$key", "url": "$url", "block": "$block", "challengeId": "$challengeId", "challengesCompleted": "$challengesCompleted", "isProject": "$isProject"}'; } + + @override + bool operator ==(covariant ChallengeViewArguments other) { + if (identical(this, other)) return true; + return other.key == key && + other.url == url && + other.block == block && + other.challengeId == challengeId && + other.challengesCompleted == challengesCompleted && + other.isProject == isProject; + } + + @override + int get hashCode { + return key.hashCode ^ + url.hashCode ^ + block.hashCode ^ + challengeId.hashCode ^ + challengesCompleted.hashCode ^ + isProject.hashCode; + } } class NativeLoginViewArguments { @@ -469,6 +574,17 @@ class NativeLoginViewArguments { String toString() { return '{"key": "$key", "fromButton": "$fromButton"}'; } + + @override + bool operator ==(covariant NativeLoginViewArguments other) { + if (identical(this, other)) return true; + return other.key == key && other.fromButton == fromButton; + } + + @override + int get hashCode { + return key.hashCode ^ fromButton.hashCode; + } } class SuperBlockViewArguments { @@ -491,6 +607,23 @@ class SuperBlockViewArguments { String toString() { return '{"key": "$key", "superBlockDashedName": "$superBlockDashedName", "superBlockName": "$superBlockName", "hasInternet": "$hasInternet"}'; } + + @override + bool operator ==(covariant SuperBlockViewArguments other) { + if (identical(this, other)) return true; + return other.key == key && + other.superBlockDashedName == superBlockDashedName && + other.superBlockName == superBlockName && + other.hasInternet == hasInternet; + } + + @override + int get hashCode { + return key.hashCode ^ + superBlockDashedName.hashCode ^ + superBlockName.hashCode ^ + hasInternet.hashCode; + } } extension NavigatorStateExtension on _i23.NavigationService { diff --git a/mobile-app/lib/enums/dialog_type.dart b/mobile-app/lib/enums/dialog_type.dart index acf2f5996..00cde2a98 100644 --- a/mobile-app/lib/enums/dialog_type.dart +++ b/mobile-app/lib/enums/dialog_type.dart @@ -1,4 +1,5 @@ enum DialogType { basic, buttonForm, + deleteAccount } diff --git a/mobile-app/lib/ui/views/settings/delete-account/delete_account_view.dart b/mobile-app/lib/ui/views/settings/delete-account/delete_account_view.dart index 37edee1d1..29c12da2f 100644 --- a/mobile-app/lib/ui/views/settings/delete-account/delete_account_view.dart +++ b/mobile-app/lib/ui/views/settings/delete-account/delete_account_view.dart @@ -70,30 +70,6 @@ class DeleteAccountView extends StatelessWidget { const SizedBox( height: 12, ), - // Row( - // children: [ - // Expanded( - // child: TextButton( - // onPressed: () => Navigator.pop(context), - // style: TextButton.styleFrom( - // backgroundColor: const Color(0xFF0a0a23), - // side: const BorderSide( - // width: 2, - // color: Colors.white, - // ), - // ), - // child: const Text( - // "Nevermind, I don't want to delete my account", - // textAlign: TextAlign.center, - // style: paragraphTextStyle, - // ), - // ), - // ), - // ], - // ), - // const SizedBox( - // height: 12, - // ), Row( children: [ Expanded( diff --git a/mobile-app/lib/ui/views/settings/delete-account/delete_account_viewmodel.dart b/mobile-app/lib/ui/views/settings/delete-account/delete_account_viewmodel.dart index 1f943dba7..0f8c6820c 100644 --- a/mobile-app/lib/ui/views/settings/delete-account/delete_account_viewmodel.dart +++ b/mobile-app/lib/ui/views/settings/delete-account/delete_account_viewmodel.dart @@ -3,6 +3,7 @@ import 'dart:developer'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:freecodecamp/app/app.locator.dart'; +import 'package:freecodecamp/enums/dialog_type.dart'; import 'package:freecodecamp/service/authentication/authentication_service.dart'; import 'package:stacked/stacked.dart'; import 'package:stacked_services/stacked_services.dart'; @@ -11,70 +12,82 @@ class DeleteAccountViewModel extends BaseViewModel { final _authenticationService = locator(); final _navigator = locator(); final _snackbar = locator(); + final _dialogService = locator(); final Dio _dio = Dio(); bool processing = false; void deleteAccount(BuildContext context) async { processing = true; - showDialog( - context: context, - barrierDismissible: false, - routeSettings: const RouteSettings( - name: 'Delete account processing', - ), - builder: (context) { - return WillPopScope( - onWillPop: () async => false, - child: const SimpleDialog( - title: Text('Deleting account...'), - contentPadding: EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 24.0), - backgroundColor: Color(0xFF2A2A40), - children: [ - Center( - child: CircularProgressIndicator(), - ), - ], - ), - ); - }, + DialogResponse? res = await _dialogService.showCustomDialog( + barrierDismissible: true, + variant: DialogType.deleteAccount, + title: 'Delete account', + description: + 'Are you sure you want to delete your account? - this deletes everything related to your account', + mainButtonTitle: 'Delete account', ); - notifyListeners(); - try { - Response res = await _dio.post( - '${AuthenticationService.baseApiURL}/account/delete', - options: Options( - headers: { - 'CSRF-Token': _authenticationService.csrfToken, - 'Cookie': - 'jwt_access_token=${_authenticationService.jwtAccessToken}; _csrf=${_authenticationService.csrf};', - }, + if (res?.confirmed == true) { + showDialog( + context: context, + barrierDismissible: false, + routeSettings: const RouteSettings( + name: 'Delete account processing', ), + builder: (context) { + return WillPopScope( + onWillPop: () async => false, + child: const SimpleDialog( + title: Text('Deleting account...'), + contentPadding: EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 24.0), + backgroundColor: Color(0xFF2A2A40), + children: [ + Center( + child: CircularProgressIndicator(), + ), + ], + ), + ); + }, ); + notifyListeners(); - if (res.statusCode == 200) { - log('Account deleted'); - await _authenticationService.logout(); - _navigator.clearStackAndShow('/'); - _snackbar.showSnackbar( - title: 'Your account has been successfully deleted', - message: '', + try { + Response res = await _dio.post( + '${AuthenticationService.baseApiURL}/account/delete', + options: Options( + headers: { + 'CSRF-Token': _authenticationService.csrfToken, + 'Cookie': + 'jwt_access_token=${_authenticationService.jwtAccessToken}; _csrf=${_authenticationService.csrf};', + }, + ), ); - } else { - log('Account deletion failed'); + + if (res.statusCode == 200) { + log('Account deleted'); + await _authenticationService.logout(); + _navigator.clearStackAndShow('/'); + _snackbar.showSnackbar( + title: 'Your account has been successfully deleted', + message: '', + ); + } else { + log('Account deletion failed'); + _navigator.back(); + _snackbar.showSnackbar( + title: 'Account deletion failed. Please try again later.', + message: '', + ); + } + } catch (err) { _navigator.back(); _snackbar.showSnackbar( title: 'Account deletion failed. Please try again later.', message: '', ); } - } catch (err) { - _navigator.back(); - _snackbar.showSnackbar( - title: 'Account deletion failed. Please try again later.', - message: '', - ); } processing = false; diff --git a/mobile-app/lib/ui/widgets/setup_dialog_ui.dart b/mobile-app/lib/ui/widgets/setup_dialog_ui.dart index 7a38c2d9a..2a70fa6c3 100644 --- a/mobile-app/lib/ui/widgets/setup_dialog_ui.dart +++ b/mobile-app/lib/ui/widgets/setup_dialog_ui.dart @@ -14,6 +14,9 @@ void setupDialogUi() { DialogType.buttonForm: (BuildContext context, DialogRequest sheetRequest, Function(DialogResponse) completer) => _buttonDialog(request: sheetRequest, onDialogTap: completer), + DialogType.deleteAccount: (BuildContext context, DialogRequest sheetRequest, + Function(DialogResponse) completer) => + _deleteAccountDialog(request: sheetRequest, onDialogTap: completer), }; dialogService.registerCustomDialogBuilders(builders); @@ -108,6 +111,103 @@ class _buttonDialog extends HookWidget { } } +// ignore: camel_case_types +class _deleteAccountDialog extends HookWidget { + final DialogRequest request; + final Function(DialogResponse) onDialogTap; + + const _deleteAccountDialog( + {Key? key, required this.request, required this.onDialogTap}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Dialog( + backgroundColor: const Color(0xFF0a0a23), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.all(16), + child: Wrap( + runSpacing: 20, + children: [ + Text( + request.title as String, + style: const TextStyle( + fontSize: 24, + height: 1.5, + fontWeight: FontWeight.bold, + ), + ), + Text( + request.description as String, + style: const TextStyle( + height: 1.5, + fontSize: 16, + ), + ), + SizedBox( + height: 50, + width: MediaQuery.of(context).size.width, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(0), + ), + backgroundColor: Colors.red.shade100, + foregroundColor: Colors.red.shade900, + side: BorderSide( + width: 2, + color: Colors.red.shade900, + ), + disabledBackgroundColor: Colors.red.shade50, + disabledForegroundColor: Colors.red.shade700, + ), + onPressed: () => { + onDialogTap(DialogResponse(confirmed: true)), + }, + child: Text( + request.mainButtonTitle?.toUpperCase() as String, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + SizedBox( + height: 50, + width: MediaQuery.of(context).size.width, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: + const Color.fromRGBO(0x3b, 0x3b, 0x4f, 1), + side: const BorderSide(width: 2, color: Colors.white), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(0), + ), + ), + onPressed: () => { + onDialogTap(DialogResponse(confirmed: false)), + }, + child: Text( + request.secondaryButtonTitle ?? 'Cancel', + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 16), + ), + ), + ), + ], + ), + ) + ], + ), + ); + } +} + // ignore: camel_case_types, unused_element class _buttonDialog2 extends HookWidget { final DialogRequest request; diff --git a/mobile-app/pubspec.yaml b/mobile-app/pubspec.yaml index 58421b6cf..a71e8611f 100644 --- a/mobile-app/pubspec.yaml +++ b/mobile-app/pubspec.yaml @@ -1,7 +1,7 @@ name: freecodecamp description: freecodecamp.org app. publish_to: none -version: 3.4.1+30401 +version: 3.5.0+30500 environment: sdk: ">=2.12.0 <3.0.0" dependencies: