Skip to content

Commit

Permalink
feat: [PE-628] CGN - deactivate button on expired card detail screen (#…
Browse files Browse the repository at this point in the history
…5992)

## Short description
Add deactivate button on expired card detail screen

## List of changes proposed in this pull request
- add i18n key
- refactor original deactivation button into hook + component
- create a new button for deactivating (original section not modified to
not disrupt existing validations)

## How to test


[figma](https://www.figma.com/design/yOoBGDr1MJpa0LY5lDUFSd/IOapp_CartaGiovaniNazionale?node-id=6171-28477&t=1CciY9sMGswSE3dR-4)

- you need to have an expired CGN card in your wallet (possibile steps
to reproduce)
  - login into the app
  - go to wallet
  - add CGN
- at this point the server should return { "status" : "EXPIRED", ... }
on GET /api/v1/cgn/status
- login into the app
- go to you wallet
- go to CGN card
- at this point there should be an alert stating that the card is
expired and the new button for deactivating the card should be there

---------

Co-authored-by: Alessandro <[email protected]>
Co-authored-by: mariateresaventura <[email protected]>
  • Loading branch information
3 people authored Aug 23, 2024
1 parent ea4c74b commit 10738b2
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 107 deletions.
1 change: 1 addition & 0 deletions locales/de/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2121,6 +2121,7 @@ bonus:
alert:
title: "Karte deaktivieren"
message: "Vuoi davvero disattivare la tua Carta Giovani Nazionale? Se confermi, non potrai più accedere alle opportunità e la carta scomparirà dal Portafoglio."
expired: Elimina Carta Giovani Nazionale
otp:
error: "Aufgrund eines technischen Problems war es uns nicht möglich, einen Rabattcode zu generieren. Bitte versuche es erneut."
code:
Expand Down
1 change: 1 addition & 0 deletions locales/en/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2770,6 +2770,7 @@ bonus:
alert:
title: "Deactivate card"
message: "Do you really want to deactivate your Carta Giovani Nazionale? If you confirm, you will no longer be able to access the opportunities and the card will disappear from your Wallet."
expired: Delete Carta Giovani Nazionale
otp:
error: "Due to a technical problem we were unable to generate a discount code. Please try again."
code:
Expand Down
1 change: 1 addition & 0 deletions locales/it/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2770,6 +2770,7 @@ bonus:
alert:
title: "Disattiva carta"
message: "Vuoi davvero disattivare la tua Carta Giovani Nazionale? Se confermi, non potrai più accedere alle opportunità e la carta scomparirà dal Portafoglio."
expired: Elimina Carta Giovani Nazionale
otp:
error: "Per un problema tecnico non siamo riusciti a generare un codice sconto. Ti chiediamo di riprovare."
code:
Expand Down
48 changes: 3 additions & 45 deletions ts/features/bonus/cgn/components/detail/CgnUnsubscribe.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,11 @@
import * as React from "react";
import { useEffect, useRef } from "react";
import { Alert } from "react-native";
import { IOToast, ListItemAction } from "@pagopa/io-app-design-system";
import { ListItemAction } from "@pagopa/io-app-design-system";

import { useIODispatch, useIOSelector } from "../../../../../store/hooks";
import { cgnUnsubscribeSelector } from "../../store/reducers/unsubscribe";
import I18n from "../../../../../i18n";
import { cgnUnsubscribe } from "../../store/actions/unsubscribe";
import { isError, isReady } from "../../../../../common/model/RemoteValue";
import { cgnDetails } from "../../store/actions/details";
import { useIONavigation } from "../../../../../navigation/params/AppParamsList";
import { useCgnUnsubscribe } from "../../hooks/useCgnUnsubscribe";

const CgnUnsubscribe = () => {
const dispatch = useIODispatch();
const navigation = useIONavigation();
const unsubscriptionStatus = useIOSelector(cgnUnsubscribeSelector);
const isFirstRender = useRef<boolean>(true);

const requestUnsubscription = () => {
Alert.alert(
I18n.t("bonus.cgn.activation.deactivate.alert.title"),
I18n.t("bonus.cgn.activation.deactivate.alert.message"),
[
{
text: I18n.t("global.buttons.cancel"),
style: "cancel"
},
{
text: I18n.t("global.buttons.deactivate"),
onPress: () => dispatch(cgnUnsubscribe.request())
}
]
);
};

useEffect(() => {
if (isReady(unsubscriptionStatus)) {
navigation.goBack();
dispatch(cgnDetails.request());
IOToast.success(I18n.t("bonus.cgn.activation.deactivate.toast"));
}
if (isError(unsubscriptionStatus) && !isFirstRender.current) {
IOToast.error(I18n.t("global.genericError"));
}

// eslint-disable-next-line functional/immutable-data
isFirstRender.current = false;
}, [unsubscriptionStatus, navigation, dispatch]);

const { requestUnsubscription } = useCgnUnsubscribe();
return (
<ListItemAction
accessibilityLabel={I18n.t("bonus.cgn.cta.deactivateBonus")}
Expand Down
50 changes: 50 additions & 0 deletions ts/features/bonus/cgn/hooks/useCgnUnsubscribe.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useEffect, useRef } from "react";
import { Alert } from "react-native";
import { IOToast } from "@pagopa/io-app-design-system";
import { useIODispatch, useIOSelector } from "../../../../store/hooks";
import { cgnUnsubscribeSelector } from "../store/reducers/unsubscribe";
import I18n from "../../../../i18n";
import { cgnUnsubscribe } from "../store/actions/unsubscribe";
import { isError, isReady } from "../../../../common/model/RemoteValue";
import { cgnDetails } from "../store/actions/details";
import { useIONavigation } from "../../../../navigation/params/AppParamsList";

export function useCgnUnsubscribe() {
const dispatch = useIODispatch();
const navigation = useIONavigation();
const unsubscriptionStatus = useIOSelector(cgnUnsubscribeSelector);
const isFirstRender = useRef<boolean>(true);

const requestUnsubscription = () => {
Alert.alert(
I18n.t("bonus.cgn.activation.deactivate.alert.title"),
I18n.t("bonus.cgn.activation.deactivate.alert.message"),
[
{
text: I18n.t("global.buttons.cancel"),
style: "cancel"
},
{
text: I18n.t("global.buttons.deactivate"),
onPress: () => dispatch(cgnUnsubscribe.request())
}
]
);
};

useEffect(() => {
if (isReady(unsubscriptionStatus)) {
navigation.goBack();
dispatch(cgnDetails.request());
IOToast.success(I18n.t("bonus.cgn.activation.deactivate.toast"));
}
if (isError(unsubscriptionStatus) && !isFirstRender.current) {
IOToast.error(I18n.t("global.genericError"));
}

// eslint-disable-next-line functional/immutable-data
isFirstRender.current = false;
}, [unsubscriptionStatus, navigation, dispatch]);

return { requestUnsubscription };
}
163 changes: 101 additions & 62 deletions ts/features/bonus/cgn/screens/CgnDetailScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import CgnCardComponent from "../components/detail/CgnCardComponent";
import CgnOwnershipInformation from "../components/detail/CgnOwnershipInformation";
import CgnStatusDetail from "../components/detail/CgnStatusDetail";
import CgnUnsubscribe from "../components/detail/CgnUnsubscribe";
import { useCgnUnsubscribe } from "../hooks/useCgnUnsubscribe";
import EycaDetailComponent from "../components/detail/eyca/EycaDetailComponent";
import { navigateToCgnMerchantsTabs } from "../navigation/actions";
import { CgnDetailsParamsList } from "../navigation/params";
Expand Down Expand Up @@ -79,11 +80,6 @@ const gradientSafeArea: IOSpacingScale = 80;
const contentEndMargin: IOSpacingScale = 32;
const spaceBetweenActions: IOSpacer = 24;

const getPrimaryactionLabel = (isEycaEnabled: boolean) =>
isEycaEnabled
? I18n.t("bonus.cgn.detail.cta.buyers")
: I18n.t("bonus.cgn.detail.cta.discover");

/**
* Screen to display all the information about the active CGN
*/
Expand Down Expand Up @@ -116,22 +112,6 @@ const CgnDetailScreen = (props: Props): React.ReactElement => {
props.isCgnEnabled &&
CardActivated.is(props.cgnDetails);

const gradientAreaHeight: number = React.useMemo(
() =>
bottomMargin +
buttonSolidHeight +
gradientSafeArea +
(canDisplayEycaDetails ? buttonSolidHeight : 0),
[bottomMargin, canDisplayEycaDetails]
);

const footerGradientOpacityTransition = useAnimatedStyle(() => ({
opacity: withTiming(gradientOpacity.value, {
duration: 200,
easing: Easing.ease
})
}));

const scrollHandler = useAnimatedScrollHandler(
({ contentOffset, layoutMeasurement, contentSize }) => {
// eslint-disable-next-line functional/immutable-data
Expand Down Expand Up @@ -171,14 +151,6 @@ const CgnDetailScreen = (props: Props): React.ReactElement => {
return true;
});

const onPressShowCgnDiscounts = () => {
if (props.isMerchantV2Enabled) {
props.navigateToMerchantsTabs();
} else {
navigation.navigate(CGN_ROUTES.DETAILS.MERCHANTS.CATEGORIES);
}
};

return (
<LoadingSpinnerOverlay
isLoading={
Expand Down Expand Up @@ -262,39 +234,12 @@ const CgnDetailScreen = (props: Props): React.ReactElement => {
</View>
</Animated.ScrollView>
<SectionStatusComponent sectionKey={"cgn"} />
{props.isCgnEnabled &&
props.cgnDetails?.status === StatusEnum.ACTIVATED && (
<GradientBottomActions
primaryActionProps={{
label: getPrimaryactionLabel(canDisplayEycaDetails),
onPress: onPressShowCgnDiscounts
}}
secondaryActionProps={
canDisplayEycaDetails
? {
label: I18n.t(
"bonus.cgn.detail.cta.eyca.showEycaDiscounts"
),
accessibilityLabel: I18n.t(
"bonus.cgn.detail.cta.eyca.showEycaDiscounts"
),
onPress: () =>
openWebUrl(EYCA_WEBSITE_DISCOUNTS_PAGE_URL, () =>
IOToast.error(I18n.t("bonus.cgn.generic.linkError"))
)
}
: undefined
}
transitionAnimStyle={footerGradientOpacityTransition}
dimensions={{
bottomMargin,
extraBottomMargin: 0,
gradientAreaHeight,
spaceBetweenActions,
safeBackgroundHeight: bottomMargin
}}
/>
)}
<FooterActions
{...props}
canDisplayEycaDetails={canDisplayEycaDetails}
bottomMargin={bottomMargin}
gradientOpacity={gradientOpacity}
/>
</>
)}
</LoadingSpinnerOverlay>
Expand All @@ -320,3 +265,97 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({
});

export default connect(mapStateToProps, mapDispatchToProps)(CgnDetailScreen);

type FooterActionsProps = Props & {
canDisplayEycaDetails: boolean;
bottomMargin: number;
gradientOpacity: Animated.SharedValue<number>;
};

function FooterActions({
canDisplayEycaDetails,
bottomMargin,
gradientOpacity,
...props
}: FooterActionsProps) {
const navigation =
useNavigation<IOStackNavigationProp<CgnDetailsParamsList, "CGN_DETAILS">>();

const onPressShowCgnDiscounts = () => {
if (props.isMerchantV2Enabled) {
props.navigateToMerchantsTabs();
} else {
navigation.navigate(CGN_ROUTES.DETAILS.MERCHANTS.CATEGORIES);
}
};

const { requestUnsubscription } = useCgnUnsubscribe();

const footerGradientOpacityTransition = useAnimatedStyle(() => ({
opacity: withTiming(gradientOpacity.value, {
duration: 200,
easing: Easing.ease
})
}));

const gradientAreaHeight: number = React.useMemo(
() =>
bottomMargin +
buttonSolidHeight +
gradientSafeArea +
(canDisplayEycaDetails ? buttonSolidHeight : 0),
[bottomMargin, canDisplayEycaDetails]
);

return props.isCgnEnabled &&
props.cgnDetails?.status === StatusEnum.ACTIVATED ? (
<GradientBottomActions
primaryActionProps={{
label: canDisplayEycaDetails
? I18n.t("bonus.cgn.detail.cta.buyers")
: I18n.t("bonus.cgn.detail.cta.discover"),
onPress: onPressShowCgnDiscounts
}}
secondaryActionProps={
canDisplayEycaDetails
? {
label: I18n.t("bonus.cgn.detail.cta.eyca.showEycaDiscounts"),
accessibilityLabel: I18n.t(
"bonus.cgn.detail.cta.eyca.showEycaDiscounts"
),
onPress: () =>
openWebUrl(EYCA_WEBSITE_DISCOUNTS_PAGE_URL, () =>
IOToast.error(I18n.t("bonus.cgn.generic.linkError"))
)
}
: undefined
}
transitionAnimStyle={footerGradientOpacityTransition}
dimensions={{
bottomMargin,
extraBottomMargin: 0,
gradientAreaHeight,
spaceBetweenActions,
safeBackgroundHeight: bottomMargin
}}
/>
) : (
CardExpired.is(props.cgnDetails) && (
<GradientBottomActions
primaryActionProps={{
color: "danger",
label: I18n.t("bonus.cgn.activation.deactivate.expired"),
onPress: requestUnsubscription
}}
transitionAnimStyle={footerGradientOpacityTransition}
dimensions={{
bottomMargin,
extraBottomMargin: 0,
gradientAreaHeight,
spaceBetweenActions,
safeBackgroundHeight: bottomMargin
}}
/>
)
);
}

0 comments on commit 10738b2

Please sign in to comment.