diff --git a/ts/boot/configureStoreAndPersistor.ts b/ts/boot/configureStoreAndPersistor.ts index 26b33faa001..70fd3f4591d 100644 --- a/ts/boot/configureStoreAndPersistor.ts +++ b/ts/boot/configureStoreAndPersistor.ts @@ -1,6 +1,6 @@ import * as pot from "@pagopa/ts-commons/lib/pot"; -import * as O from "fp-ts/lib/Option"; import AsyncStorage from "@react-native-async-storage/async-storage"; +import * as O from "fp-ts/lib/Option"; import _, { merge } from "lodash"; import { applyMiddleware, @@ -23,11 +23,13 @@ import { } from "redux-persist"; import createSagaMiddleware from "redux-saga"; import { remoteUndefined } from "../common/model/RemoteValue"; +import { FeaturesState } from "../features/common/store/reducers"; import { CURRENT_REDUX_LOLLIPOP_STORE_VERSION } from "../features/lollipop/store"; import { initialLollipopState, LollipopState } from "../features/lollipop/store/reducers/lollipop"; +import { PaymentsState as PaymentsFeatureState } from "../features/payments/common/store/reducers"; import rootSaga from "../sagas"; import { Action, StoreEnhancer } from "../store/actions/types"; import { analytics } from "../store/middlewares"; @@ -38,8 +40,8 @@ import { import { ContentState } from "../store/reducers/content"; import { entitiesPersistConfig } from "../store/reducers/entities"; import { - InstallationState, - INSTALLATION_INITIAL_STATE + INSTALLATION_INITIAL_STATE, + InstallationState } from "../store/reducers/installation"; import { NotificationsState } from "../store/reducers/notifications"; import { getInitialState as getInstallationInitialState } from "../store/reducers/notifications/installation"; @@ -356,6 +358,24 @@ const migrations: MigrationManifest = { ..._.omit(persistedPreferences, "isExperimentalFeaturesEnabled") } }; + }, + // Version 24 + // Adds payments history archive persistence + "24": (state: PersistedState) => { + const features: FeaturesState = (state as PersistedGlobalState).features; + const payments: PaymentsFeatureState = features.payments; + return { + ...state, + features: { + ...features, + payments: { + ...payments, + history: { + archive: [] + } + } + } + }; } }; diff --git a/ts/features/payments/history/store/actions/index.ts b/ts/features/payments/history/store/actions/index.ts index 81309882185..a3a66209289 100644 --- a/ts/features/payments/history/store/actions/index.ts +++ b/ts/features/payments/history/store/actions/index.ts @@ -1,10 +1,15 @@ import { ActionType, createStandardAction } from "typesafe-actions"; import { WalletPaymentOutcome } from "../../../payment/types/PaymentOutcomeEnum"; +import { RptId } from "../../../../../../definitions/pagopa/ecommerce/RptId"; + +export const walletPaymentStoreNewAttempt = createStandardAction( + "WALLET_PAYMENT_STORE_NEW_ATTEMPT" +)(); export const walletPaymentHistoryStoreOutcome = createStandardAction( "WALLET_PAYMENT_HISTORY_STORE_OUTCOME" )(); -export type WalletPaymentHistoryActions = ActionType< - typeof walletPaymentHistoryStoreOutcome ->; +export type WalletPaymentHistoryActions = + | ActionType + | ActionType; diff --git a/ts/features/payments/history/store/reducers/index.ts b/ts/features/payments/history/store/reducers/index.ts index ba206cb70b1..2b41959af3c 100644 --- a/ts/features/payments/history/store/reducers/index.ts +++ b/ts/features/payments/history/store/reducers/index.ts @@ -1,4 +1,5 @@ import * as O from "fp-ts/lib/Option"; +import * as A from "fp-ts/lib/Array"; import { pipe } from "fp-ts/lib/function"; import _ from "lodash"; import { AsyncStorage } from "react-native"; @@ -16,7 +17,11 @@ import { import { walletPaymentInitState } from "../../../payment/store/actions/orchestration"; import { WalletPaymentFailure } from "../../../payment/types/WalletPaymentFailure"; import { PaymentHistory } from "../../types"; -import { walletPaymentHistoryStoreOutcome } from "../actions"; +import { + walletPaymentStoreNewAttempt, + walletPaymentHistoryStoreOutcome +} from "../actions"; +import { RptId } from "../../../../../../definitions/pagopa/ecommerce/RptId"; export type WalletPaymentHistoryState = { ongoingPayment?: PaymentHistory; @@ -44,17 +49,24 @@ const reducer = ( } }; case getType(walletPaymentGetDetails.request): - return updatePaymentHistory( - state, - { - rptId: action.payload - }, - true - ); + return { + ...state, + ongoingPayment: { + ...state.ongoingPayment, + rptId: action.payload, + attempt: getPaymentAttemptByRptId(state, action.payload) + } + }; case getType(walletPaymentGetDetails.success): - return updatePaymentHistory(state, { - verifiedData: action.payload - }); + return { + ...state, + ongoingPayment: { + ...state.ongoingPayment, + verifiedData: action.payload + } + }; + case getType(walletPaymentStoreNewAttempt): + return updatePaymentHistory(state, {}, true); case getType(walletPaymentCreateTransaction.success): case getType(walletPaymentGetTransactionInfo.success): return updatePaymentHistory(state, { @@ -81,6 +93,17 @@ const reducer = ( return state; }; +const getPaymentAttemptByRptId = ( + state: WalletPaymentHistoryState, + rptId: RptId +) => + pipe( + state.archive as Array, + A.findFirst(h => h.rptId === rptId), + O.chainNullableK(h => h.attempt), + O.getOrElse(() => 0) + ); + const appendItemToArchive = ( archive: ReadonlyArray, item: PaymentHistory @@ -98,14 +121,16 @@ const appendItemToArchive = ( const updatePaymentHistory = ( state: WalletPaymentHistoryState, data: PaymentHistory, - reset: boolean = false + newAttempt: boolean = false ): WalletPaymentHistoryState => { - const updatedOngoingPaymentHistory = { + const currentAttempt = state.ongoingPayment?.attempt || 0; + const updatedOngoingPaymentHistory: PaymentHistory = { ...state.ongoingPayment, - ...data + ...data, + attempt: newAttempt ? currentAttempt + 1 : currentAttempt }; - if (reset) { + if (newAttempt) { return { ongoingPayment: updatedOngoingPaymentHistory, archive: appendItemToArchive(state.archive, updatedOngoingPaymentHistory) diff --git a/ts/features/payments/history/store/selectors/index.ts b/ts/features/payments/history/store/selectors/index.ts index 637905494af..93d158d55d8 100644 --- a/ts/features/payments/history/store/selectors/index.ts +++ b/ts/features/payments/history/store/selectors/index.ts @@ -1,3 +1,7 @@ +import * as O from "fp-ts/lib/Option"; +import { pipe } from "fp-ts/lib/function"; +import { createSelector } from "reselect"; +import { RptId } from "../../../../../../definitions/pagopa/ecommerce/RptId"; import { GlobalState } from "../../../../../store/reducers/types"; export const selectWalletPaymentHistoryArchive = (state: GlobalState) => @@ -5,3 +9,22 @@ export const selectWalletPaymentHistoryArchive = (state: GlobalState) => export const selectWalletOngoingPaymentHistory = (state: GlobalState) => state.features.payments.history.ongoingPayment; + +export const walletPaymentAttemptByRptSelector = (rptId: RptId) => + createSelector(selectWalletPaymentHistoryArchive, archive => + pipe( + O.fromNullable(archive.find(h => h.rptId === rptId)), + O.chainNullableK(h => h.attempt), + O.getOrElse(() => 0) + ) + ); + +export const walletOngoingPaymentAttemptSelector = createSelector( + selectWalletOngoingPaymentHistory, + paymentHistory => + pipe( + O.fromNullable(paymentHistory), + O.chainNullableK(h => h.attempt), + O.getOrElse(() => 0) + ) +); diff --git a/ts/features/payments/history/types/index.ts b/ts/features/payments/history/types/index.ts index 8330e80094e..22a4999eff5 100644 --- a/ts/features/payments/history/types/index.ts +++ b/ts/features/payments/history/types/index.ts @@ -14,4 +14,5 @@ export type PaymentHistory = { transaction?: NewTransactionResponse; outcome?: WalletPaymentOutcomeEnum; failure?: WalletPaymentFailure; + attempt?: number; }; diff --git a/ts/features/payments/payment/hooks/useWalletPaymentAuthorizationModal.tsx b/ts/features/payments/payment/hooks/useWalletPaymentAuthorizationModal.tsx index b92de68c239..51caffdd3bf 100644 --- a/ts/features/payments/payment/hooks/useWalletPaymentAuthorizationModal.tsx +++ b/ts/features/payments/payment/hooks/useWalletPaymentAuthorizationModal.tsx @@ -7,6 +7,7 @@ import * as React from "react"; import URLParse from "url-parse"; import { useIODispatch, useIOSelector } from "../../../../store/hooks"; import { WALLET_WEBVIEW_OUTCOME_SCHEMA } from "../../common/utils/const"; +import { walletPaymentHistoryStoreOutcome } from "../../history/store/actions"; import { WalletPaymentAuthorizePayload, walletPaymentAuthorization @@ -16,7 +17,6 @@ import { WalletPaymentOutcome, WalletPaymentOutcomeEnum } from "../types/PaymentOutcomeEnum"; -import { walletPaymentHistoryStoreOutcome } from "../../history/store/actions"; type Props = { onAuthorizationOutcome: (outcome: WalletPaymentOutcome) => void; @@ -35,6 +35,7 @@ export const useWalletPaymentAuthorizationModal = ({ onDismiss }: Props): WalletPaymentAuthorizationModal => { const dispatch = useIODispatch(); + const authorizationUrlPot = useIOSelector( walletPaymentAuthorizationUrlSelector ); @@ -52,8 +53,8 @@ export const useWalletPaymentAuthorizationModal = ({ WalletPaymentOutcome.decode, E.getOrElse(() => WalletPaymentOutcomeEnum.GENERIC_ERROR) ); - dispatch(walletPaymentHistoryStoreOutcome(outcome)); onAuthorizationOutcome(outcome); + dispatch(walletPaymentHistoryStoreOutcome(outcome)); }, [onAuthorizationOutcome, dispatch] ); diff --git a/ts/features/payments/payment/screens/WalletPaymentDetailScreen.tsx b/ts/features/payments/payment/screens/WalletPaymentDetailScreen.tsx index 3ad6e0846f4..14225986ee6 100644 --- a/ts/features/payments/payment/screens/WalletPaymentDetailScreen.tsx +++ b/ts/features/payments/payment/screens/WalletPaymentDetailScreen.tsx @@ -51,6 +51,7 @@ import { WalletPaymentRoutes } from "../navigation/routes"; import { walletPaymentGetDetails } from "../store/actions/networking"; import { walletPaymentDetailsSelector } from "../store/selectors"; import { WalletPaymentFailure } from "../types/WalletPaymentFailure"; +import { walletPaymentStoreNewAttempt } from "../../history/store/actions"; type WalletPaymentDetailScreenNavigationParams = { rptId: RptId; @@ -117,6 +118,7 @@ const WalletPaymentDetailContent = ({ rptId, payment }: WalletPaymentDetailContentProps) => { + const dispatch = useIODispatch(); const navigation = useNavigation>(); useLayoutEffect(() => { @@ -132,6 +134,7 @@ const WalletPaymentDetailContent = ({ }); const navigateToMakePaymentScreen = () => { + dispatch(walletPaymentStoreNewAttempt(rptId)); navigation.push(WalletPaymentRoutes.WALLET_PAYMENT_MAIN, { screen: WalletPaymentRoutes.WALLET_PAYMENT_MAKE }); diff --git a/ts/features/payments/payment/store/reducers/index.ts b/ts/features/payments/payment/store/reducers/index.ts index 4c049691a54..13275e05bea 100644 --- a/ts/features/payments/payment/store/reducers/index.ts +++ b/ts/features/payments/payment/store/reducers/index.ts @@ -7,12 +7,9 @@ import { PaymentMethodsResponse } from "../../../../../../definitions/pagopa/eco import { PaymentRequestsGetResponse } from "../../../../../../definitions/pagopa/ecommerce/PaymentRequestsGetResponse"; import { RptId } from "../../../../../../definitions/pagopa/ecommerce/RptId"; import { TransactionInfo } from "../../../../../../definitions/pagopa/ecommerce/TransactionInfo"; -import { WalletInfo } from "../../../../../../definitions/pagopa/ecommerce/WalletInfo"; -import { Wallets } from "../../../../../../definitions/pagopa/ecommerce/Wallets"; import { Action } from "../../../../../store/actions/types"; import { NetworkError } from "../../../../../utils/errors"; import { PaymentStartRoute } from "../../types"; -import { WalletPaymentFailure } from "../../types/WalletPaymentFailure"; import { walletPaymentAuthorization, walletPaymentCalculateFees, @@ -32,6 +29,9 @@ import { walletPaymentResetPickedPsp, walletPaymentSetCurrentStep } from "../actions/orchestration"; +import { WalletPaymentFailure } from "../../types/WalletPaymentFailure"; +import { Wallets } from "../../../../../../definitions/pagopa/ecommerce/Wallets"; +import { WalletInfo } from "../../../../../../definitions/pagopa/ecommerce/WalletInfo"; export const WALLET_PAYMENT_STEP_MAX = 4;