diff --git a/ts/features/payments/payment/components/WalletPaymentConfirmContent.tsx b/ts/features/payments/payment/components/WalletPaymentConfirmContent.tsx index 456914c71e8..2d9484c1f09 100644 --- a/ts/features/payments/payment/components/WalletPaymentConfirmContent.tsx +++ b/ts/features/payments/payment/components/WalletPaymentConfirmContent.tsx @@ -7,15 +7,11 @@ import { VSpacer } from "@pagopa/io-app-design-system"; import { openAuthenticationSession } from "@pagopa/io-react-native-login-utils"; -import { useNavigation } from "@react-navigation/native"; import React from "react"; import { Bundle } from "../../../../../definitions/pagopa/ecommerce/Bundle"; import { PaymentRequestsGetResponse } from "../../../../../definitions/pagopa/ecommerce/PaymentRequestsGetResponse"; import I18n from "../../../../i18n"; -import { - AppParamsList, - IOStackNavigationProp -} from "../../../../navigation/params/AppParamsList"; +import { useIODispatch } from "../../../../store/hooks"; import { format } from "../../../../utils/dates"; import { formatNumberCentsToAmount } from "../../../../utils/stringBuilder"; import { capitalize } from "../../../../utils/strings"; @@ -24,7 +20,7 @@ import { getPaymentLogo } from "../../common/utils"; import { UIWalletInfoDetails } from "../../details/types/UIWalletInfoDetails"; -import { WalletPaymentRoutes } from "../navigation/routes"; +import { walletPaymentSetCurrentStep } from "../store/actions/orchestration"; import { WalletPaymentTotalAmount } from "./WalletPaymentTotalAmount"; export type WalletPaymentConfirmContentProps = { @@ -42,7 +38,7 @@ export const WalletPaymentConfirmContent = ({ isLoading, onConfirm }: WalletPaymentConfirmContentProps) => { - const navigation = useNavigation>(); + const dispatch = useIODispatch(); const taxFee = selectedPsp.taxPayerFee ?? 0; @@ -76,11 +72,7 @@ export const WalletPaymentConfirmContent = ({ paymentLogo={getPaymentLogo(paymentMethodDetails)} title={getPaymentTitle(paymentMethodDetails)} subtitle={getPaymentSubtitle(paymentMethodDetails)} - onPress={() => - navigation.navigate(WalletPaymentRoutes.WALLET_PAYMENT_MAIN, { - screen: WalletPaymentRoutes.WALLET_PAYMENT_PICK_METHOD - }) - } + onPress={() => dispatch(walletPaymentSetCurrentStep(1))} /> - navigation.navigate(WalletPaymentRoutes.WALLET_PAYMENT_MAIN, { - screen: WalletPaymentRoutes.WALLET_PAYMENT_PICK_PSP - }) - } + onPress={() => dispatch(walletPaymentSetCurrentStep(2))} /> diff --git a/ts/features/payments/payment/components/WalletPaymentHeader.tsx b/ts/features/payments/payment/components/WalletPaymentHeader.tsx new file mode 100644 index 00000000000..7137438d19f --- /dev/null +++ b/ts/features/payments/payment/components/WalletPaymentHeader.tsx @@ -0,0 +1,70 @@ +import { + ActionProp, + HeaderSecondLevel, + Stepper, + VSpacer +} from "@pagopa/io-app-design-system"; +import React from "react"; +import { useStartSupportRequest } from "../../../../hooks/useStartSupportRequest"; +import I18n from "../../../../i18n"; +import { useIONavigation } from "../../../../navigation/params/AppParamsList"; +import { useIODispatch } from "../../../../store/hooks"; +import { emptyContextualHelp } from "../../../../utils/emptyContextualHelp"; +import { useWalletPaymentGoBackHandler } from "../hooks/useWalletPaymentGoBackHandler"; +import { walletPaymentSetCurrentStep } from "../store/actions/orchestration"; +import { useHardwareBackButton } from "../../../../hooks/useHardwareBackButton"; +import { WALLET_PAYMENT_STEP_MAX } from "../store/reducers"; + +type WalletPaymentHeaderProps = { + currentStep: number; +}; + +const WalletPaymentHeader = ({ currentStep }: WalletPaymentHeaderProps) => { + const navigation = useIONavigation(); + const dispatch = useIODispatch(); + const goBackHandler = useWalletPaymentGoBackHandler(); + + const startSupportRequest = useStartSupportRequest({ + faqCategories: ["payment"], + contextualHelp: emptyContextualHelp + }); + + const handleGoBack = React.useCallback(() => { + if (currentStep === 1 && goBackHandler) { + return goBackHandler(); + } + + if (currentStep === 1) { + return navigation.goBack(); + } + + dispatch(walletPaymentSetCurrentStep(currentStep - 1)); + }, [navigation, dispatch, goBackHandler, currentStep]); + + useHardwareBackButton(() => { + handleGoBack(); + return true; + }); + + return ( + <> + + + + + ); +}; + +export { WalletPaymentHeader }; diff --git a/ts/features/payments/payment/navigation/navigator.tsx b/ts/features/payments/payment/navigation/navigator.tsx index 4bcfa3c2d8f..1b99cf45b09 100644 --- a/ts/features/payments/payment/navigation/navigator.tsx +++ b/ts/features/payments/payment/navigation/navigator.tsx @@ -8,10 +8,8 @@ import { isGestureEnabled } from "../../../../utils/navigation"; import { WalletPaymentDetailScreen } from "../screens/WalletPaymentDetailScreen"; import { WalletPaymentInputFiscalCodeScreen } from "../screens/WalletPaymentInputFiscalCodeScreen"; import { WalletPaymentInputNoticeNumberScreen } from "../screens/WalletPaymentInputNoticeNumberScreen"; -import { WalletPaymentPickMethodScreen } from "../screens/WalletPaymentPickMethodScreen"; +import { WalletPaymentMakeScreen } from "../screens/WalletPaymentMakeScreen"; import { WalletPaymentOutcomeScreen } from "../screens/WalletPaymentOutcomeScreen"; -import { WalletPaymentPickPspScreen } from "../screens/WalletPaymentPickPspScreen"; -import { WalletPaymentConfirmScreen } from "../screens/WalletPaymentConfirmScreen"; import { WalletPaymentParamsList } from "./params"; import { WalletPaymentRoutes } from "./routes"; @@ -48,24 +46,11 @@ export const WalletPaymentNavigator = () => ( }} /> - - { - const navigation = useNavigation>(); + const navigation = useIONavigation(); const paymentDetailsPot = useIOSelector(walletPaymentDetailsSelector); const transactionPot = useIOSelector(walletPaymentTransactionSelector); @@ -44,13 +38,6 @@ const WalletPaymentConfirmScreen = () => { ); const selectedPspOption = useIOSelector(walletPaymentPickedPspSelector); - useHeaderSecondLevel({ - title: "", - contextualHelp: emptyContextualHelp, - faqCategories: ["payment"], - supportRequest: true - }); - const handleStartPaymentAuthorization = () => pipe( sequenceS(O.Monad)({ diff --git a/ts/features/payments/payment/screens/WalletPaymentDetailScreen.tsx b/ts/features/payments/payment/screens/WalletPaymentDetailScreen.tsx index 3fb4894035e..3ad6e0846f4 100644 --- a/ts/features/payments/payment/screens/WalletPaymentDetailScreen.tsx +++ b/ts/features/payments/payment/screens/WalletPaymentDetailScreen.tsx @@ -131,9 +131,9 @@ const WalletPaymentDetailContent = ({ contextualHelp: emptyContextualHelp }); - const navigateToMethodSelection = () => { + const navigateToMakePaymentScreen = () => { navigation.push(WalletPaymentRoutes.WALLET_PAYMENT_MAIN, { - screen: WalletPaymentRoutes.WALLET_PAYMENT_PICK_METHOD + screen: WalletPaymentRoutes.WALLET_PAYMENT_MAKE }); }; @@ -200,7 +200,7 @@ const WalletPaymentDetailContent = ({ primaryActionProps={{ label: "Vai al pagamento", accessibilityLabel: "Vai al pagmento", - onPress: navigateToMethodSelection + onPress: navigateToMakePaymentScreen }} > { + const ref = React.useRef(null); + const currentStep = useIOSelector(selectWalletPaymentCurrentStep); + + React.useEffect(() => { + ref.current?.setPage(currentStep - 1); + }, [ref, currentStep]); + + return ( + <> + + + + + + + + + + + + + + ); +}; + +const styles = StyleSheet.create({ + pagerView: { + flex: 1 + } +}); + +export { WalletPaymentMakeScreen }; diff --git a/ts/features/payments/payment/screens/WalletPaymentPickMethodScreen.tsx b/ts/features/payments/payment/screens/WalletPaymentPickMethodScreen.tsx index 8e4ec1cc8ed..0135c2f5262 100644 --- a/ts/features/payments/payment/screens/WalletPaymentPickMethodScreen.tsx +++ b/ts/features/payments/payment/screens/WalletPaymentPickMethodScreen.tsx @@ -10,7 +10,7 @@ import { VSpacer } from "@pagopa/io-app-design-system"; import * as pot from "@pagopa/ts-commons/lib/pot"; -import { useFocusEffect, useNavigation } from "@react-navigation/native"; +import { useFocusEffect } from "@react-navigation/native"; import { sequenceS, sequenceT } from "fp-ts/lib/Apply"; import * as A from "fp-ts/lib/Array"; import * as O from "fp-ts/lib/Option"; @@ -18,21 +18,16 @@ import { pipe } from "fp-ts/lib/function"; import { capitalize } from "lodash"; import React, { useEffect, useMemo } from "react"; import { View } from "react-native"; -import { PaymentMethodResponse } from "../../../../../definitions/pagopa/walletv3/PaymentMethodResponse"; +import { Transfer } from "../../../../../definitions/pagopa/ecommerce/Transfer"; import { WalletInfo } from "../../../../../definitions/pagopa/ecommerce/WalletInfo"; -import { useHeaderSecondLevel } from "../../../../hooks/useHeaderSecondLevel"; +import { PaymentMethodResponse } from "../../../../../definitions/pagopa/walletv3/PaymentMethodResponse"; import I18n from "../../../../i18n"; -import { - AppParamsList, - IOStackNavigationProp -} from "../../../../navigation/params/AppParamsList"; +import { useIONavigation } from "../../../../navigation/params/AppParamsList"; import { useIODispatch, useIOSelector } from "../../../../store/hooks"; import { ComponentProps } from "../../../../types/react"; -import { emptyContextualHelp } from "../../../../utils/emptyContextualHelp"; import { findFirstCaseInsensitive } from "../../../../utils/object"; import { UIWalletInfoDetails } from "../../details/types/UIWalletInfoDetails"; import { WalletPaymentMissingMethodsError } from "../components/WalletPaymentMissingMethodsError"; -import { useWalletPaymentGoBackHandler } from "../hooks/useWalletPaymentGoBackHandler"; import { useOnTransactionActivationEffect } from "../hooks/useOnTransactionActivationEffect"; import { WalletPaymentRoutes } from "../navigation/routes"; import { @@ -41,7 +36,10 @@ import { walletPaymentGetUserWallets, walletPaymentResetPspList } from "../store/actions/networking"; -import { walletPaymentPickPaymentMethod } from "../store/actions/orchestration"; +import { + walletPaymentPickPaymentMethod, + walletPaymentSetCurrentStep +} from "../store/actions/orchestration"; import { walletPaymentAllMethodsSelector, walletPaymentAmountSelector, @@ -53,7 +51,6 @@ import { walletPaymentUserWalletsSelector } from "../store/selectors"; import { WalletPaymentOutcomeEnum } from "../types/PaymentOutcomeEnum"; -import { Transfer } from "../../../../../definitions/pagopa/ecommerce/Transfer"; type SavedMethodState = { kind: "saved"; @@ -71,17 +68,7 @@ type SelectedMethodState = SavedMethodState | NotSavedMethodState | undefined; const WalletPaymentPickMethodScreen = () => { const dispatch = useIODispatch(); - const navigation = useNavigation>(); - const handleGoBack = useWalletPaymentGoBackHandler(); - - useHeaderSecondLevel({ - title: "", - backAccessibilityLabel: I18n.t("global.buttons.back"), - goBack: handleGoBack, - contextualHelp: emptyContextualHelp, - faqCategories: ["payment"], - supportRequest: true - }); + const navigation = useIONavigation(); const paymentDetailsPot = useIOSelector(walletPaymentDetailsSelector); const getSavedtMethodById = useIOSelector( @@ -138,17 +125,13 @@ const WalletPaymentPickMethodScreen = () => { pot.toOption, O.map(pspList => { if (pspList.length > 1) { - navigation.navigate(WalletPaymentRoutes.WALLET_PAYMENT_MAIN, { - screen: WalletPaymentRoutes.WALLET_PAYMENT_PICK_PSP - }); + dispatch(walletPaymentSetCurrentStep(2)); } else if (pspList.length >= 1) { - navigation.navigate(WalletPaymentRoutes.WALLET_PAYMENT_MAIN, { - screen: WalletPaymentRoutes.WALLET_PAYMENT_CONFIRM - }); + dispatch(walletPaymentSetCurrentStep(3)); } }) ); - }, [navigation, pspListPot]); + }, [pspListPot, dispatch]); const alertRef = React.useRef(null); @@ -260,7 +243,7 @@ const WalletPaymentPickMethodScreen = () => { } }, [isLoading, genericMethodsListItems, savedMethodsListItems]); - if (!isLoading && savedMethodsListItems.length === 0) { + if (pot.isSome(userWalletsPots) && userWalletsPots.value.length === 0) { return ; } diff --git a/ts/features/payments/payment/screens/WalletPaymentPickPspScreen.tsx b/ts/features/payments/payment/screens/WalletPaymentPickPspScreen.tsx index e4ebb4e6c9a..b0ca9e09aa0 100644 --- a/ts/features/payments/payment/screens/WalletPaymentPickPspScreen.tsx +++ b/ts/features/payments/payment/screens/WalletPaymentPickPspScreen.tsx @@ -8,19 +8,13 @@ import { VSpacer } from "@pagopa/io-app-design-system"; import * as pot from "@pagopa/ts-commons/lib/pot"; -import { useNavigation } from "@react-navigation/native"; import * as O from "fp-ts/lib/Option"; import { pipe } from "fp-ts/lib/function"; import React from "react"; import { Bundle } from "../../../../../definitions/pagopa/ecommerce/Bundle"; -import { useHeaderSecondLevel } from "../../../../hooks/useHeaderSecondLevel"; import I18n from "../../../../i18n"; -import { - AppParamsList, - IOStackNavigationProp -} from "../../../../navigation/params/AppParamsList"; +import { useIONavigation } from "../../../../navigation/params/AppParamsList"; import { useIODispatch, useIOSelector } from "../../../../store/hooks"; -import { emptyContextualHelp } from "../../../../utils/emptyContextualHelp"; import { formatNumberCentsToAmount } from "../../../../utils/stringBuilder"; import { getSortedPspList } from "../../common/utils"; import { WalletPspListSkeleton } from "../components/WalletPspListSkeleton"; @@ -28,7 +22,8 @@ import { useSortPspBottomSheet } from "../hooks/useSortPspBottomSheet"; import { WalletPaymentRoutes } from "../navigation/routes"; import { walletPaymentPickPsp, - walletPaymentResetPickedPsp + walletPaymentResetPickedPsp, + walletPaymentSetCurrentStep } from "../store/actions/orchestration"; import { walletPaymentPickedPspSelector, @@ -39,7 +34,8 @@ import { WalletPaymentOutcomeEnum } from "../types/PaymentOutcomeEnum"; const WalletPaymentPickPspScreen = () => { const dispatch = useIODispatch(); - const navigation = useNavigation>(); + const navigation = useIONavigation(); + const [showFeaturedPsp, setShowFeaturedPsp] = React.useState(true); const [sortType, setSortType] = React.useState("default"); @@ -69,13 +65,6 @@ const WalletPaymentPickPspScreen = () => { } }, [isError, navigation]); - useHeaderSecondLevel({ - title: "", - contextualHelp: emptyContextualHelp, - faqCategories: ["payment"], - supportRequest: true - }); - React.useEffect( () => () => { dispatch(walletPaymentResetPickedPsp()); @@ -114,9 +103,7 @@ const WalletPaymentPickPspScreen = () => { ); const handleContinue = () => { - navigation.navigate(WalletPaymentRoutes.WALLET_PAYMENT_MAIN, { - screen: WalletPaymentRoutes.WALLET_PAYMENT_CONFIRM - }); + dispatch(walletPaymentSetCurrentStep(3)); }; const sortButtonProps: ListItemHeader["endElement"] = React.useMemo( diff --git a/ts/features/payments/payment/store/__tests__/store.test.ts b/ts/features/payments/payment/store/__tests__/store.test.ts index b0a477837f2..88eeca605a4 100644 --- a/ts/features/payments/payment/store/__tests__/store.test.ts +++ b/ts/features/payments/payment/store/__tests__/store.test.ts @@ -1,10 +1,13 @@ import * as pot from "@pagopa/ts-commons/lib/pot"; import * as O from "fp-ts/lib/Option"; +import { createStore } from "redux"; import { applicationChangeState } from "../../../../../store/actions/application"; import { appReducer } from "../../../../../store/reducers"; -import { WalletPaymentState } from "../reducers"; +import { walletPaymentSetCurrentStep } from "../actions/orchestration"; +import { WALLET_PAYMENT_STEP_MAX, WalletPaymentState } from "../reducers"; const INITIAL_STATE: WalletPaymentState = { + currentStep: 1, sessionToken: pot.none, paymentDetails: pot.none, userWallets: pot.none, @@ -21,4 +24,22 @@ describe("Test Wallet reducer", () => { const globalState = appReducer(undefined, applicationChangeState("active")); expect(globalState.features.payments.payment).toStrictEqual(INITIAL_STATE); }); + + it("should correctly update payment step, also when trying to overflow the steps, it should set the steps to WALLET_PAYMENT_STEP_MAX, and in case zero is passed it should set the step to 1", () => { + const globalState = appReducer(undefined, applicationChangeState("active")); + expect(globalState.features.payments.payment).toStrictEqual(INITIAL_STATE); + + const store = createStore(appReducer, globalState as any); + + store.dispatch(walletPaymentSetCurrentStep(2)); + expect(store.getState().features.payments.payment.currentStep).toBe(2); + + store.dispatch(walletPaymentSetCurrentStep(20)); + expect(store.getState().features.payments.payment.currentStep).toBe( + WALLET_PAYMENT_STEP_MAX + ); + + store.dispatch(walletPaymentSetCurrentStep(0)); + expect(store.getState().features.payments.payment.currentStep).toBe(1); + }); }); diff --git a/ts/features/payments/payment/store/actions/orchestration.ts b/ts/features/payments/payment/store/actions/orchestration.ts index 306bd66920b..9f5dfb55073 100644 --- a/ts/features/payments/payment/store/actions/orchestration.ts +++ b/ts/features/payments/payment/store/actions/orchestration.ts @@ -3,6 +3,10 @@ import { Bundle } from "../../../../../../definitions/pagopa/ecommerce/Bundle"; import { WalletInfo } from "../../../../../../definitions/pagopa/ecommerce/WalletInfo"; import { PaymentStartOrigin, PaymentStartRoute } from "../../types"; +export const walletPaymentSetCurrentStep = createStandardAction( + "WALLET_PAYMENT_SET_CURRENT_STEP" +)(); + export type PaymentInitStateParams = { startOrigin?: PaymentStartOrigin; startRoute?: PaymentStartRoute; @@ -30,6 +34,7 @@ export const walletPaymentResetPickedPsp = createStandardAction( )(); export type WalletPaymentOrchestrationActions = + | ActionType | ActionType | ActionType | ActionType diff --git a/ts/features/payments/payment/store/reducers/index.ts b/ts/features/payments/payment/store/reducers/index.ts index a0412ad4596..4c049691a54 100644 --- a/ts/features/payments/payment/store/reducers/index.ts +++ b/ts/features/payments/payment/store/reducers/index.ts @@ -1,5 +1,6 @@ import * as pot from "@pagopa/ts-commons/lib/pot"; import * as O from "fp-ts/lib/Option"; +import _ from "lodash"; import { getType } from "typesafe-actions"; import { Bundle } from "../../../../../../definitions/pagopa/ecommerce/Bundle"; import { PaymentMethodsResponse } from "../../../../../../definitions/pagopa/ecommerce/PaymentMethodsResponse"; @@ -28,10 +29,14 @@ import { walletPaymentInitState, walletPaymentPickPaymentMethod, walletPaymentPickPsp, - walletPaymentResetPickedPsp + walletPaymentResetPickedPsp, + walletPaymentSetCurrentStep } from "../actions/orchestration"; +export const WALLET_PAYMENT_STEP_MAX = 4; + export type WalletPaymentState = { + currentStep: number; rptId?: RptId; sessionToken: pot.Pot; paymentDetails: pot.Pot< @@ -50,6 +55,7 @@ export type WalletPaymentState = { }; const INITIAL_STATE: WalletPaymentState = { + currentStep: 1, sessionToken: pot.none, paymentDetails: pot.none, userWallets: pot.none, @@ -74,6 +80,12 @@ const reducer = ( showTransaction: action.payload.showTransaction }; + case getType(walletPaymentSetCurrentStep): + return { + ...state, + currentStep: _.clamp(action.payload, 1, WALLET_PAYMENT_STEP_MAX) + }; + // eCommerce Session token case getType(walletPaymentNewSessionToken.request): return { diff --git a/ts/features/payments/payment/store/selectors/index.ts b/ts/features/payments/payment/store/selectors/index.ts index f3f3026428d..a0f81bca106 100644 --- a/ts/features/payments/payment/store/selectors/index.ts +++ b/ts/features/payments/payment/store/selectors/index.ts @@ -7,6 +7,9 @@ import { GlobalState } from "../../../../../store/reducers/types"; const selectWalletPayment = (state: GlobalState) => state.features.payments.payment; +export const selectWalletPaymentCurrentStep = (state: GlobalState) => + selectWalletPayment(state).currentStep; + export const selectWalletPaymentSessionTokenPot = (state: GlobalState) => selectWalletPayment(state).sessionToken;