Skip to content

Commit

Permalink
feat: [#176167278] Show message when remove profile if some bonus is …
Browse files Browse the repository at this point in the history
…active (#2597)

* [#176167278] add alert text

* [#176167278] create alert and connect with bonus information

* [#176167278] create saga to load bonus before go to remove account detail screen

* [#176167278] create navigateToRemoveAccountDetail screen action

* [#176167278] create action to start the load bonus before remove account flow

* [#176167278] dispatch action to start load bonus before remove account flow at continue button press

* [#176167278] fix typo

* [#176167278] moving remove account saga inside profile watcher

* [#176167278] check for every bonus if is available

* [#176167278] call navigation action and the bonuses information in parallel

* [#176167278] remove unused import

* [#176167278] add comment on action

* [#176167278] fix typo

* [#176167278] add comment

Co-authored-by: Matteo Boschi <[email protected]>
Co-authored-by: Cristiano Tofani <[email protected]>
  • Loading branch information
3 people authored Dec 23, 2020
1 parent d3b11ca commit 9b4d556
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 44 deletions.
4 changes: 4 additions & 0 deletions locales/en/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -282,9 +282,13 @@ profile:
description: Permanently delete your account and your personal data
error: An error occurred while submit your deletion request, please retry!
alert:
activeBonusTitle: Active bonus
activeBonusDescription: You have active Bonuses. Please be aware that deleting your profile will not delete bonuses or unsubscribe you from any initiative. To manage your bonuses go to your Wallet, otherwise choose Continue
oldRequest: We are already proceeding to delete your account.
oldRequestSubtitle: To use IO you will need to register again. The cancellation does not concern the data processed by public administrations.
cta:
manageBonus: Manage bonuses
continue: Continue
return: Return
cancel: Cancel the deletion
info:
Expand Down
4 changes: 4 additions & 0 deletions locales/it/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -282,9 +282,13 @@ profile:
description: Cancella in modo permanente il tuo account e i tuoi dati personali
error: Si è verificato un errore nella richiesta di rimozione dell'account, riprova.
alert:
activeBonusTitle: Bonus attivi
activeBonusDescription: Sembra che tu abbia dei Bonus attivi. Vogliamo informarti che la cancellazione del tuo profilo non equivale alla cancellazione dei bonus o alla disiscrizione da eventuali iniziative. Per gestire i tuoi bonus vai alla sezione Portafoglio, altrimenti scegli Continua
oldRequest: Hai già richiesto la cancellazione del tuo account.
oldRequestSubtitle: "Stiamo elaborando la tua richiesta.\n Se hai cambiato idea o hai richiesto l’eliminazione per sbaglio, clicca su “Annulla eliminazione” qui sotto."
cta:
manageBonus: Gestisci bonus
continue: Continua
return: Indietro
cancel: Annulla l'eliminazione
info:
Expand Down
69 changes: 56 additions & 13 deletions ts/sagas/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ import { tosVersion } from "../config";
import I18n from "../i18n";
import { sessionExpired } from "../store/actions/authentication";
import {
loadBonusBeforeRemoveAccount,
profileLoadFailure,
profileLoadRequest,
profileLoadSuccess,
profileUpsert,
removeAccountMotivation,
startEmailValidation
} from "../store/actions/profile";
import { profileSelector } from "../store/reducers/profile";
Expand All @@ -39,6 +41,11 @@ import { preferredLanguageSelector } from "../store/reducers/persistedPreference
import { upsertUserDataProcessing } from "../store/actions/userDataProcessing";
import { UserDataProcessingChoiceEnum } from "../../definitions/backend/UserDataProcessingChoice";
import { navigateToRemoveAccountSuccess } from "../store/actions/navigation";
import { loadAllBonusActivations } from "../features/bonus/bonusVacanze/store/actions/bonusVacanze";
import { bpdLoadActivationStatus } from "../features/bonus/bpd/store/actions/details";
import { bpdEnabledSelector } from "../features/bonus/bpd/store/reducers/details/activation";
import { getValue } from "../features/bonus/bpd/model/RemoteValue";
import { allBonusActiveSelector } from "../features/bonus/bonusVacanze/store/reducers/allActive";

// A saga to load the Profile.
export function* loadProfile(
Expand Down Expand Up @@ -253,20 +260,32 @@ function* checkLoadedProfile(
}
}

// watch for some actions about profile
export function* watchProfile(
startEmailValidationProcess: ReturnType<
typeof BackendClient
>["startEmailValidationProcess"]
): Iterator<Effect> {
// user requests to send again the email validation to profile email
yield takeLatest(
getType(startEmailValidation.request),
startEmailValidationProcessSaga,
startEmailValidationProcess
export function* handleLoadBonusBeforeRemoveAccount() {
const bpdActive: ReturnType<typeof bpdEnabledSelector> = yield select(
bpdEnabledSelector
);
// check the loaded profile
yield takeLatest(getType(profileLoadSuccess), checkLoadedProfile);

// check if there are some bpd
if (getValue(bpdActive) === undefined) {
// Load the bpd data and wait for a response
yield put(bpdLoadActivationStatus.request());

yield take([
bpdLoadActivationStatus.success,
bpdLoadActivationStatus.failure
]);
}

const bonusVacanzeBonus: ReturnType<typeof allBonusActiveSelector> = yield select(
allBonusActiveSelector
);

// check if there are some bonus vacanze
if (bonusVacanzeBonus.length === 0) {
// Load the bonus data and no wait because if there are some bonus
// they will be loaded individually
yield put(loadAllBonusActivations.request());
}
}

// watch for action of removing account
Expand All @@ -291,3 +310,27 @@ export function* handleRemoveAccount() {
yield put(navigateToRemoveAccountSuccess());
}
}

// watch for some actions about profile
export function* watchProfile(
startEmailValidationProcess: ReturnType<
typeof BackendClient
>["startEmailValidationProcess"]
): Iterator<Effect> {
// user requests to send again the email validation to profile email
yield takeLatest(
getType(startEmailValidation.request),
startEmailValidationProcessSaga,
startEmailValidationProcess
);
// check the loaded profile
yield takeLatest(getType(profileLoadSuccess), checkLoadedProfile);

// Start watching for request bonus before remove profile
yield takeLatest(
loadBonusBeforeRemoveAccount,
handleLoadBonusBeforeRemoveAccount
);
// Start watching for request of remove profile
yield takeLatest(removeAccountMotivation, handleRemoveAccount);
}
9 changes: 1 addition & 8 deletions ts/sagas/startup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,7 @@ import {
import { navigationHistoryPush } from "../store/actions/navigationHistory";
import { clearNotificationPendingMessage } from "../store/actions/notifications";
import { clearOnboarding } from "../store/actions/onboarding";
import {
clearCache,
removeAccountMotivation,
resetProfileState
} from "../store/actions/profile";
import { clearCache, resetProfileState } from "../store/actions/profile";
import { loadUserDataProcessing } from "../store/actions/userDataProcessing";
import {
idpSelector,
Expand All @@ -72,7 +68,6 @@ import {
import { previousInstallationDataDeleteSaga } from "./installation";
import { updateInstallationSaga } from "./notifications";
import {
handleRemoveAccount,
loadProfile,
watchProfile,
watchProfileRefreshRequestsSaga,
Expand Down Expand Up @@ -262,8 +257,6 @@ export function* initializeApplicationSaga(): Generator<Effect, void, any> {
backendClient.getSupportToken
);

// Sart watching for request of remove profile
yield takeEvery(removeAccountMotivation, handleRemoveAccount);
// Start watching for requests of abort the onboarding
const watchAbortOnboardingSagaTask = yield fork(watchAbortOnboardingSaga);

Expand Down
76 changes: 61 additions & 15 deletions ts/screens/profile/RemoveAccountDetailsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Content, Input, Item, Label, View } from "native-base";
import * as React from "react";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import { SafeAreaView, StyleSheet } from "react-native";
import { Alert, SafeAreaView, StyleSheet } from "react-native";
import I18n from "../../i18n";
import { ReduxProps } from "../../store/actions/types";
import { GlobalState } from "../../store/reducers/types";
Expand All @@ -23,6 +23,10 @@ import { shufflePinPadOnPayment } from "../../config";
import { userDataProcessingSelector } from "../../store/reducers/userDataProcessing";
import { LoadingErrorComponent } from "../../features/bonus/bonusVacanze/components/loadingErrorScreen/LoadingErrorComponent";
import { withKeyboard } from "../../utils/keyboard";
import { allBonusActiveSelector } from "../../features/bonus/bonusVacanze/store/reducers/allActive";
import { bpdEnabledSelector } from "../../features/bonus/bpd/store/reducers/details/activation";
import { getValue } from "../../features/bonus/bpd/model/RemoteValue";
import { navigateToWalletHome } from "../../store/actions/navigation";

type Props = ReduxProps &
ReturnType<typeof mapStateToProps> &
Expand Down Expand Up @@ -68,22 +72,59 @@ const RemoveAccountDetails: React.FunctionComponent<Props> = (props: Props) => {

const [otherMotivation, setOtherMotivation] = React.useState<string>("");

const handleContinuePress = () => {
const hasActiveBonus =
props.bvActiveBonus || getValue(props.bpdActiveBonus);

if (hasActiveBonus) {
Alert.alert(
I18n.t("profile.main.privacy.removeAccount.alert.activeBonusTitle"),
I18n.t(
"profile.main.privacy.removeAccount.alert.activeBonusDescription"
),
[
{
text: I18n.t(
"profile.main.privacy.removeAccount.alert.cta.manageBonus"
),
style: "default",
onPress: props.navigateToWalletHomeScreen
},
{
text: I18n.t(
"profile.main.privacy.removeAccount.alert.cta.continue"
),
style: "cancel",
onPress: () => {
handleSendMotivation(selectedMotivation);
}
}
]
);
} else {
handleSendMotivation(selectedMotivation);
}
};

const handleSendMotivation = (
selectedMotivation: RemoveAccountMotivationEnum
) => {
switch (selectedMotivation) {
case RemoveAccountMotivationEnum.OTHERS:
// Only the "others" reason allow to insert a custom text
props.requestIdentification({
reason: selectedMotivation,
userText: otherMotivation
});
break;
default:
props.requestIdentification({ reason: selectedMotivation });
}
};
const continueButtonProps = {
block: true,
primary: true,
onPress: () => {
switch (selectedMotivation) {
case RemoveAccountMotivationEnum.OTHERS:
// Only the "others" reason allow to insert a custom text
props.requestIdentification({
reason: selectedMotivation,
userText: otherMotivation
});
break;
default:
props.requestIdentification({ reason: selectedMotivation });
}
},
onPress: handleContinuePress,
title: I18n.t("profile.main.privacy.removeAccount.info.cta")
};

Expand Down Expand Up @@ -180,17 +221,22 @@ const mapDispatchToProps = (dispatch: Dispatch) => {
},
shufflePinPadOnPayment
)
)
),
navigateToWalletHomeScreen: () => dispatch(navigateToWalletHome())
};
};

const mapStateToProps = (state: GlobalState) => {
const bpdActiveBonus = bpdEnabledSelector(state);
const bvActiveBonus = allBonusActiveSelector(state).length > 0;
const userDataProcessing = userDataProcessingSelector(state);
const isLoading =
pot.isLoading(userDataProcessing.DELETE) ||
pot.isUpdating(userDataProcessing.DELETE);
const isError = pot.isError(userDataProcessing.DELETE);
return {
bvActiveBonus,
bpdActiveBonus,
userDataProcessing,
isLoading,
isError
Expand Down
23 changes: 15 additions & 8 deletions ts/screens/profile/RemoveAccountInfoScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { Content } from "native-base";
import * as React from "react";
import { SafeAreaView } from "react-native";

import { NavigationScreenProps } from "react-navigation";
import { Dispatch } from "redux";
import { connect } from "react-redux";
import { IOStyles } from "../../components/core/variables/IOStyles";
import BaseScreenComponent from "../../components/screens/BaseScreenComponent";
import FooterWithButtons from "../../components/ui/FooterWithButtons";
import I18n from "../../i18n";
import { H1 } from "../../components/core/typography/H1";
import { H4 } from "../../components/core/typography/H4";
import ROUTES from "../../navigation/routes";
import { loadBonusBeforeRemoveAccount } from "../../store/actions/profile";
import { navigateToRemoveAccountDetailScreen } from "../../store/actions/navigation";

type Props = NavigationScreenProps;
type Props = ReturnType<typeof mapDispatchToProps>;

/**
* A screen to explain how the account removal works.
Expand All @@ -21,8 +22,10 @@ const RemoveAccountInfo: React.FunctionComponent<Props> = props => {
const continueButtonProps = {
block: true,
primary: true,
onPress: () =>
props.navigation.navigate(ROUTES.PROFILE_REMOVE_ACCOUNT_DETAILS),
onPress: () => {
props.loadBonus();
props.navigateToRemoveAccountDetail();
},
title: I18n.t("profile.main.privacy.removeAccount.info.cta")
};

Expand All @@ -46,5 +49,9 @@ const RemoveAccountInfo: React.FunctionComponent<Props> = props => {
</BaseScreenComponent>
);
};

export default RemoveAccountInfo;
const mapDispatchToProps = (dispatch: Dispatch) => ({
loadBonus: () => dispatch(loadBonusBeforeRemoveAccount()),
navigateToRemoveAccountDetail: () =>
dispatch(navigateToRemoveAccountDetailScreen())
});
export default connect(undefined, mapDispatchToProps)(RemoveAccountInfo);
5 changes: 5 additions & 0 deletions ts/store/actions/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ export const navigateToRemoveAccountSuccess = () =>
routeName: ROUTES.PROFILE_REMOVE_ACCOUNT_SUCCESS
});

export const navigateToRemoveAccountDetailScreen = () =>
NavigationActions.navigate({
routeName: ROUTES.PROFILE_REMOVE_ACCOUNT_DETAILS
});

export const navigateToPrivacyScreen = NavigationActions.navigate({
routeName: ROUTES.PROFILE_PRIVACY_MAIN,
action: NavigationActions.navigate({ routeName: ROUTES.PROFILE_PRIVACY_MAIN })
Expand Down
8 changes: 8 additions & 0 deletions ts/store/actions/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ export const profileFirstLogin = createStandardAction("PROFILE_FIRST_LOGIN")();

export const clearCache = createStandardAction("CLEAR_CACHE")();

// This action is needed because we want to show an alert if a user has some active bonus
// and he wants to delete his account.
// In this case ,if the bonuses data are not loaded yet, we use this action to
// start a saga and request the bonus information.
export const loadBonusBeforeRemoveAccount = createStandardAction(
"LOAD_BONUS_BEFORE_REMOVE_ACCOUNT"
)<void>();

export enum RemoveAccountMotivationEnum {
"UNDEFINED" = "undefined",
"NOT_UTILS" = "notUtils",
Expand Down

0 comments on commit 9b4d556

Please sign in to comment.