diff --git a/lib/bloc/account/account_bloc.dart b/lib/bloc/account/account_bloc.dart index dcf3a02..0d7c242 100644 --- a/lib/bloc/account/account_bloc.dart +++ b/lib/bloc/account/account_bloc.dart @@ -6,6 +6,8 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:meta/meta.dart'; +import '../../service/customer_service.dart'; + part 'account_event.dart'; part 'account_state.dart'; @@ -21,6 +23,7 @@ class AccountBloc extends Bloc { on(_onLogout); on(_onGetTrainees); on(_onSelectTrainees); + on(_onDeleteAccount); } void _load() { @@ -56,6 +59,24 @@ class AccountBloc extends Bloc { emit(AccountReady()); } + void _onDeleteAccount(DeleteAccount event, Emitter emit) async { + emit(AccountLoading()); + await Cache().logout(); + customerRepository.customer = event.customer; + customerRepository.emptyTrainees(); + loggedIn = false; + + //delete local store + int customerId = customerRepository.customer!.customerId!; + await Cache().deleteCustomerId(customerId); + customerRepository.customer = null; + + // deactivate user + await CustomerApi().deactivateCustomer(customerId); + + emit(AccountReady()); + } + void _onGetTrainees(AccountGetTrainees event, Emitter emit) async { emit(AccountLoading()); await customerRepository.getTrainees(); diff --git a/lib/bloc/account/account_event.dart b/lib/bloc/account/account_event.dart index 616884b..86bea19 100644 --- a/lib/bloc/account/account_event.dart +++ b/lib/bloc/account/account_event.dart @@ -20,6 +20,14 @@ class AccountLogin extends AccountEvent { const AccountLogin(); } +class DeleteAccount extends AccountEvent { + final Customer customer; + + const DeleteAccount ({required this.customer}); + @override + List get props => [customer]; +} + class AccountLogInFinished extends AccountEvent { final Customer customer; diff --git a/lib/bloc/login/login_bloc.dart b/lib/bloc/login/login_bloc.dart index 702ce3f..662bcc3 100644 --- a/lib/bloc/login/login_bloc.dart +++ b/lib/bloc/login/login_bloc.dart @@ -95,6 +95,7 @@ class LoginBloc extends Bloc with Trans { Cache().setLoginType(LoginType.email); emit(LoginSuccess()); } on Exception catch (e) { + print("Login error: $e" ); emit(LoginError(message: e.toString())); } } diff --git a/lib/model/cache.dart b/lib/model/cache.dart index d56f444..355b617 100644 --- a/lib/model/cache.dart +++ b/lib/model/cache.dart @@ -212,6 +212,12 @@ class Cache with Logging { return this.authToken; } + Future deleteCustomerId(int customerId) async { + Future prefs = SharedPreferences.getInstance(); + SharedPreferences sharedPreferences = await prefs; + sharedPreferences.remove(Cache.customerIdKey); + } + Future saveActiveExercisePlan(ExercisePlan exercisePlan, List exercisePlanDetails) async { this.activeExercisePlan = exercisePlan; this.activeExercisePlanDetails = exercisePlanDetails; diff --git a/lib/repository/user_repository.dart b/lib/repository/user_repository.dart index f679e7a..50ad151 100644 --- a/lib/repository/user_repository.dart +++ b/lib/repository/user_repository.dart @@ -204,12 +204,15 @@ class UserRepository with Logging { Future getUser() async { final User modelUser = this.user; String rc = await FirebaseApi().signInEmail(modelUser.email, modelUser.password); - - if (rc == FirebaseApi.SIGN_IN_OK) { - await CustomerApi().getUserByEmail(modelUser.email!); - await Cache().afterFirebaseLogin(); - } else { - log("Exception: user not found or password is wrong"); + try { + if (rc == FirebaseApi.SIGN_IN_OK) { + await CustomerApi().getUserByEmail(modelUser.email!); + await Cache().afterFirebaseLogin(); + } else { + log("Exception: user not found or password is wrong"); + throw Exception("Customer does not exist or the password is wrong"); + } + } on NotFoundException catch (_) { throw Exception("Customer does not exist or the password is wrong"); } } diff --git a/lib/service/api.dart b/lib/service/api.dart index 35a3dac..47f1336 100644 --- a/lib/service/api.dart +++ b/lib/service/api.dart @@ -92,6 +92,8 @@ class APIClient with Common, Logging { } else { throw Exception("Network Error, please try again later"); } + } on NotFoundException catch(e) { + throw NotFoundException(message: "Not Found"); } on Exception catch (e) { print("Post Exception: $e"); await Sentry.captureException(e); @@ -128,6 +130,8 @@ class APIClient with Common, Logging { } else { throw Exception("Network Error, please try again later"); } + } on NotFoundException catch(e) { + throw NotFoundException(message: "Not Found"); } on Exception catch (e) { print("Post Exception: $e"); await Sentry.captureException(e); diff --git a/lib/service/customer_service.dart b/lib/service/customer_service.dart index 2f3e684..49373cf 100644 --- a/lib/service/customer_service.dart +++ b/lib/service/customer_service.dart @@ -32,6 +32,11 @@ class CustomerApi with Logging { await _client.post("customers/update_firebase_uid/" + customerId.toString(), uid); } + Future deactivateCustomer(int customerId) async { + log(" ===== deactivate : $customerId"); + await _client.post("customers/deactivate/$customerId", ""); + } + Future addCustomer(Customer customer) async { customer.dateAdd = DateTime.now(); customer.dateChange = DateTime.now(); @@ -80,7 +85,7 @@ class CustomerApi with Logging { final String responseBody = await _client.get("customers/find_by_email/" + email, ""); Customer customer; try { - customer = Customer.fromJson(jsonDecode(responseBody)); + customer = Customer.fromJson(jsonDecode(responseBody)); if (customer.firebaseUid == null) { await this.updateFirebaseUid(customer.customerId!, Cache().firebaseUid!); } diff --git a/lib/util/enums.dart b/lib/util/enums.dart index 8f68e89..4e5e4ea 100644 --- a/lib/util/enums.dart +++ b/lib/util/enums.dart @@ -53,6 +53,7 @@ enum TrackingEvent { tutorial_activate, terms_of_use, data_privacy, + delete_account, faq, training_plan_open, training_plan_start, @@ -60,7 +61,7 @@ enum TrackingEvent { training_plan_finished, training_plan_custom, trial, - feedback_email + feedback_email, } T enumFromString(Iterable values, String value) { diff --git a/lib/view/account.dart b/lib/view/account.dart index 0bd17da..595fcb6 100644 --- a/lib/view/account.dart +++ b/lib/view/account.dart @@ -14,6 +14,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:firebase_in_app_messaging/firebase_in_app_messaging.dart'; +import '../util/enums.dart'; +import '../util/track.dart'; + // ignore: must_be_immutable class AccountPage extends StatelessWidget with Trans { // ignore: close_sinks @@ -152,6 +155,9 @@ class AccountPage extends StatelessWidget with Trans { ), devices(context, accountBloc), loginOut(context, accountBloc), + Divider(), + Divider(), + deleteAccount(accountBloc), //messaging(), //getMyTrainees(context, accountBloc), ]); @@ -241,6 +247,29 @@ class AccountPage extends StatelessWidget with Trans { return element; } + ListTile deleteAccount(AccountBloc accountBloc) { + return ListTile( + leading: Icon(Icons.delete_forever), + title: TextButton( + child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Text(t("Delete Account"), style: TextStyle(color: Color.fromARGB(255, 202, 10, 10))), + + ]), + style: TextButton.styleFrom( + backgroundColor: Colors.white70, + disabledForegroundColor: Colors.grey, + ), + onPressed: () => { + if (accountBloc.loggedIn) + { + deleteAccountConfirmationDialog(accountBloc), + } + + }, + ), + ); + } + Widget getMyTrainees(BuildContext context, AccountBloc accountBloc) { if (accountBloc.customerRepository.customer == null || accountBloc.customerRepository.customer!.trainer == 0) { return ListTile( @@ -303,6 +332,35 @@ class AccountPage extends StatelessWidget with Trans { )); } + void deleteAccountConfirmationDialog(AccountBloc accountBloc) { + showCupertinoDialog( + useRootNavigator: true, + context: context, + builder: (_) => CupertinoAlertDialog( + title: Text(t("Are you sure to delete your account?")), + content: Column(children: [ + Divider(), + Text(t("Your training data, historical data, goals, 1RMs will be completly lost!")), + ]), + actions: [ + TextButton( + child: Text(t("No")), + onPressed: () => Navigator.pop(context), + ), + TextButton( + child: Text(t("Yes")), + onPressed: () => { + if (accountBloc.customerRepository.customer != null && accountBloc.customerRepository.customer!.customerId != null) { + Track().track(TrackingEvent.delete_account), + accountBloc.add(DeleteAccount(customer: accountBloc.customerRepository.customer!)), + }, + Navigator.pop(context), + }, + ) + ], + )); + } + void confirmationDialog(AccountBloc accountBloc) { showCupertinoDialog( useRootNavigator: true, diff --git a/pubspec.yaml b/pubspec.yaml index 1eade59..8d27e4e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.27+115 +version: 1.1.27+116 environment: sdk: ">=2.18.4 <3.0.0"