Skip to content

Commit

Permalink
Merge branch 'master' into SFEQS-2071-refactor-new-ds-component
Browse files Browse the repository at this point in the history
  • Loading branch information
hevelius authored Jan 24, 2024
2 parents 2763380 + 3e0f184 commit a5d0382
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 19 deletions.
5 changes: 5 additions & 0 deletions locales/en/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,11 @@ wallet:
title: Vuoi pagare in app?
content: Scopri quali sono i metodi che puoi aggiungere al tuo portafoglio.
cta: Scopri di più
error:
title: Si è verificato un errore
subtitle: Riprova o contatta l'assistenza
primaryButton: Chiudi
secondaryButton: Riprova
wallet: Wallet
refreshWallet: Refresh the Wallet
favourite:
Expand Down
5 changes: 5 additions & 0 deletions locales/it/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,11 @@ wallet:
title: Vuoi pagare in app?
content: Scopri quali sono i metodi che puoi aggiungere al tuo portafoglio.
cta: Scopri di più
error:
title: Si è verificato un errore
subtitle: Riprova o contatta l'assistenza
primaryButton: Chiudi
secondaryButton: Riprova
wallet: Portafoglio
refreshWallet: Aggiorna il Portafoglio
favourite:
Expand Down
36 changes: 33 additions & 3 deletions ts/features/walletV3/details/screens/WalletDetailsScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import * as React from "react";
import { RouteProp, useRoute } from "@react-navigation/native";
import { RouteProp, useNavigation, useRoute } from "@react-navigation/native";
import { useDispatch } from "react-redux";
import { IOLogoPaymentExtType } from "@pagopa/io-app-design-system";

import * as O from "fp-ts/lib/Option";
import { pipe } from "fp-ts/lib/function";
import LoadingSpinnerOverlay from "../../../../components/LoadingSpinnerOverlay";
import WorkunitGenericFailure from "../../../../components/error/WorkunitGenericFailure";
import { PaymentCardBig } from "../../../../components/ui/cards/payment/PaymentCardBig";
import { useIOSelector } from "../../../../store/hooks";
import { idPayAreInitiativesFromInstrumentLoadingSelector } from "../../../idpay/wallet/store/reducers";
Expand All @@ -22,6 +21,12 @@ import {
import { walletDetailsGetInstrument } from "../store/actions";
import { UIWalletInfoDetails } from "../types/UIWalletInfoDetails";
import { getDateFromExpiryDate } from "../../../../utils/dates";
import { OperationResultScreenContent } from "../../../../components/screens/OperationResultScreenContent";
import I18n from "../../../../i18n";
import {
AppParamsList,
IOStackNavigationProp
} from "../../../../navigation/params/AppParamsList";

export type WalletDetailsScreenNavigationParams = Readonly<{
walletId: string;
Expand Down Expand Up @@ -71,6 +76,7 @@ const generateCardHeaderTitle = (details?: UIWalletInfoDetails) => {
*/
const WalletDetailsScreen = () => {
const route = useRoute<WalletDetailsScreenRouteProps>();
const navigation = useNavigation<IOStackNavigationProp<AppParamsList>>();
const dispatch = useDispatch();
const { walletId } = route.params;
const walletDetails = useIOSelector(walletDetailsInstrumentSelector);
Expand All @@ -82,6 +88,30 @@ const WalletDetailsScreen = () => {
idPayAreInitiativesFromInstrumentLoadingSelector
);

const WalletDetailsGenericFailure = () => (
<OperationResultScreenContent
title={I18n.t("wallet.methodDetails.error.title")}
subtitle={I18n.t("wallet.methodDetails.error.subtitle")}
pictogram="umbrellaNew"
action={{
label: I18n.t("wallet.methodDetails.error.primaryButton"),
accessibilityLabel: I18n.t("wallet.methodDetails.error.primaryButton"),
onPress: () => navigation.pop()
}}
secondaryAction={{
label: I18n.t("wallet.methodDetails.error.secondaryButton"),
accessibilityLabel: I18n.t(
"wallet.methodDetails.error.secondaryButton"
),
onPress: handleOnRetry
}}
/>
);

const handleOnRetry = () => {
dispatch(walletDetailsGetInstrument.request({ walletId }));
};

React.useEffect(() => {
dispatch(walletDetailsGetInstrument.request({ walletId }));
}, [walletId, dispatch]);
Expand Down Expand Up @@ -122,7 +152,7 @@ const WalletDetailsScreen = () => {
</LoadingSpinnerOverlay>
);
} else if (isErrorWalletDetails) {
return <WorkunitGenericFailure />;
return <WalletDetailsGenericFailure />;
}
return null;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ import { formatNumberCentsToAmount } from "../../../../utils/stringBuilder";
import { WalletPaymentFeebackBanner } from "../components/WalletPaymentFeedbackBanner";
import { usePaymentFailureSupportModal } from "../hooks/usePaymentFailureSupportModal";
import { WalletPaymentParamsList } from "../navigation/params";
import { walletPaymentDetailsSelector } from "../store/selectors";
import {
walletPaymentDetailsSelector,
walletPaymentStartRouteSelector
} from "../store/selectors";
import {
WalletPaymentOutcome,
WalletPaymentOutcomeEnum
Expand All @@ -38,6 +41,7 @@ const WalletPaymentOutcomeScreen = () => {

const navigation = useNavigation<IOStackNavigationProp<AppParamsList>>();
const paymentDetailsPot = useIOSelector(walletPaymentDetailsSelector);
const paymentStartRoute = useIOSelector(walletPaymentStartRouteSelector);

const supportModal = usePaymentFailureSupportModal({
outcome
Expand All @@ -55,6 +59,12 @@ const WalletPaymentOutcomeScreen = () => {
};

const handleClose = () => {
if (paymentStartRoute) {
navigation.navigate(paymentStartRoute.routeName, {
screen: paymentStartRoute.routeKey
});
return;
}
navigation.popToTop();
navigation.pop();
};
Expand Down
4 changes: 4 additions & 0 deletions ts/features/walletV3/payment/store/actions/orchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { ActionType, createStandardAction } from "typesafe-actions";
import { Bundle } from "../../../../../../definitions/pagopa/ecommerce/Bundle";
import { WalletInfo } from "../../../../../../definitions/pagopa/walletv3/WalletInfo";

/**
* Action to initialize the state of a payment, optionally you can specify the route to go back to
* after the payment is completed or cancelled (default is the popToTop route)
*/
export const walletPaymentInitState = createStandardAction(
"WALLET_PAYMENT_INIT_STATE"
)();
Expand Down
25 changes: 24 additions & 1 deletion ts/features/walletV3/payment/store/reducers/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import _ from "lodash";
import * as pot from "@pagopa/ts-commons/lib/pot";
import * as O from "fp-ts/lib/Option";
import { NavigatorScreenParams } from "@react-navigation/native";
import { getType } from "typesafe-actions";
import { pipe } from "fp-ts/lib/function";
import { sequenceS } from "fp-ts/lib/Apply";
import { Bundle } from "../../../../../../definitions/pagopa/ecommerce/Bundle";
import { NewTransactionResponse } from "../../../../../../definitions/pagopa/ecommerce/NewTransactionResponse";
import { PaymentRequestsGetResponse } from "../../../../../../definitions/pagopa/ecommerce/PaymentRequestsGetResponse";
Expand All @@ -27,6 +30,8 @@ import {
import { WalletInfo } from "../../../../../../definitions/pagopa/walletv3/WalletInfo";
import { WalletPaymentFailure } from "../../types/failure";
import { RptId } from "../../../../../../definitions/pagopa/ecommerce/RptId";
import NavigationService from "../../../../../navigation/NavigationService";
import { AppParamsList } from "../../../../../navigation/params/AppParamsList";

export type WalletPaymentState = {
rptId?: RptId;
Expand All @@ -44,6 +49,10 @@ export type WalletPaymentState = {
NetworkError | WalletPaymentFailure
>;
authorizationUrl: pot.Pot<string, NetworkError>;
startRoute?: {
routeName: keyof AppParamsList;
routeKey: keyof NavigatorScreenParams<AppParamsList>["screen"];
};
};

const INITIAL_STATE: WalletPaymentState = {
Expand All @@ -64,7 +73,21 @@ const reducer = (
): WalletPaymentState => {
switch (action.type) {
case getType(walletPaymentInitState):
return INITIAL_STATE;
const startRoute = pipe(
sequenceS(O.Monad)({
routeName: O.fromNullable(
NavigationService.getCurrentRouteName() as keyof AppParamsList
),
routeKey: O.fromNullable(
NavigationService.getCurrentRouteKey() as keyof NavigatorScreenParams<AppParamsList>["screen"]
)
}),
O.toUndefined
);
return {
...INITIAL_STATE,
startRoute
};

// Payment verification and details
case getType(walletPaymentGetDetails.request):
Expand Down
5 changes: 5 additions & 0 deletions ts/features/walletV3/payment/store/selectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,8 @@ export const walletPaymentAuthorizationUrlSelector = createSelector(
selectWalletPayment,
state => state.authorizationUrl
);

export const walletPaymentStartRouteSelector = createSelector(
selectWalletPayment,
state => state.startRoute
);
25 changes: 22 additions & 3 deletions ts/navigation/AppStackNavigator.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
/* eslint-disable functional/immutable-data */
import { LinkingOptions, NavigationContainer } from "@react-navigation/native";
import {
LinkingOptions,
NavigationContainer,
NavigationContainerProps
} from "@react-navigation/native";
import * as React from "react";
import { useRef } from "react";
import { View } from "react-native";
Expand Down Expand Up @@ -38,10 +42,22 @@ import { useStoredExperimentalDesign } from "../common/context/DSExperimentalCon
import { IONavigationLightTheme } from "../theme/navigations";
import { MESSAGES_ROUTES } from "../features/messages/navigation/routes";
import AuthenticatedStackNavigator from "./AuthenticatedStackNavigator";
import NavigationService, { navigationRef } from "./NavigationService";
import NavigationService, {
navigationRef,
setMainNavigatorReady
} from "./NavigationService";
import NotAuthenticatedStackNavigator from "./NotAuthenticatedStackNavigator";
import ROUTES from "./routes";

type OnStateChangeStateType = Parameters<
NonNullable<NavigationContainerProps["onStateChange"]>
>[0];
const isMainNavigatorReady = (state: OnStateChangeStateType) =>
state &&
state.routes &&
state.routes.length > 0 &&
state.routes[0].name === ROUTES.MAIN;

export const AppStackNavigator = (): React.ReactElement => {
// This hook is used since we are in a child of the Context Provider
// to setup the experimental design system value from AsyncStorage
Expand Down Expand Up @@ -153,7 +169,10 @@ const InnerNavigationContainer = (props: { children: React.ReactElement }) => {
NavigationService.setNavigationReady();
routeNameRef.current = navigationRef.current?.getCurrentRoute()?.name;
}}
onStateChange={async () => {
onStateChange={async state => {
if (isMainNavigatorReady(state)) {
setMainNavigatorReady();
}
const previousRouteName = routeNameRef.current;
const currentRouteName = navigationRef.current?.getCurrentRoute()?.name;
if (currentRouteName !== undefined) {
Expand Down
12 changes: 11 additions & 1 deletion ts/navigation/NavigationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ export const navigationRef = React.createRef<NavigationContainerRef>();
// eslint-disable-next-line functional/no-let
let isNavigationReady: boolean = false;

// eslint-disable-next-line functional/no-let
let isMainNavigatorReady = false;

export const setMainNavigatorReady = () => {
isMainNavigatorReady = true;
};

export const setNavigationReady = () => {
// eslint-disable-next-line functional/immutable-data
isNavigationReady = true;
Expand Down Expand Up @@ -44,6 +51,8 @@ const dispatchNavigationAction = (action: NavigationAction) => {
navigationRef.current?.dispatch(action);
};

const getIsMainNavigatorReady = () => isMainNavigatorReady;

const getCurrentRouteName = (): string | undefined =>
navigationRef.current?.getCurrentRoute()?.name;

Expand All @@ -66,5 +75,6 @@ export default {
getCurrentRoute,
getCurrentState,
getIsNavigationReady,
setNavigationReady
setNavigationReady,
getIsMainNavigatorReady
};
42 changes: 32 additions & 10 deletions ts/sagas/startup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,13 +426,11 @@ export function* initializeApplicationSaga(
const watchAbortOnboardingSagaTask = yield* fork(watchAbortOnboardingSaga);

yield* put(startupLoadSuccess(StartupStatusEnum.ONBOARDING));
// FIXME IOPID-1298: find any better way to handle this
// We need this workaround to let the inner AppStackNavigator stack be ready,
// before continuing with any other navigation action to avoid:
// Error: The 'navigation' object hasn't been initialized yet...
// Here the navigationRef is ready, but because we changed the navigation inner stack
// based on StartupStatusEnum value, we need to wait for the new stack to be ready.
yield* delay(0 as Millisecond);
if (!handleSessionExpiration) {
yield* call(waitForMainNavigator);
}

// yield* delay(0 as Millisecond);
const hasPreviousSessionAndPin =
previousSessionToken && O.isSome(maybeStoredPin);
if (hasPreviousSessionAndPin && showIdentificationModal) {
Expand Down Expand Up @@ -517,9 +515,6 @@ export function* initializeApplicationSaga(
yield* call(updateInstallationSaga, backendClient.createOrUpdateInstallation);

yield* put(startupLoadSuccess(StartupStatusEnum.AUTHENTICATED));
// FIXME IOPID-1298: find any better way to handle this
// As above for StartupStatusEnum.ONBOARDING
yield* delay(0 as Millisecond);
//
// User is autenticated, session token is valid
//
Expand Down Expand Up @@ -695,6 +690,33 @@ function* waitForNavigatorServiceInitialization() {
});
}

function* waitForMainNavigator() {
// eslint-disable-next-line functional/no-let
let isMainNavReady = yield* call(NavigationService.getIsMainNavigatorReady);

// eslint-disable-next-line functional/no-let
let timeoutLogged = false;
const startTime = performance.now();

// before continuing we must wait for the main navigator tack to be ready
while (!isMainNavReady) {
const elapsedTime = performance.now() - startTime;
if (!timeoutLogged && elapsedTime >= warningWaitNavigatorTime) {
timeoutLogged = true;

yield* call(mixpanelTrack, "MAIN_NAVIGATOR_STACK_READY_TIMEOUT");
}
yield* delay(navigatorPollingTime);
isMainNavReady = yield* call(NavigationService.getIsMainNavigatorReady);
}

const initTime = performance.now() - startTime;

yield* call(mixpanelTrack, "MAIN_NAVIGATOR_STACK_READY_OK", {
elapsedTime: initTime
});
}

/**
* Remove all the local notifications related to authentication with spid.
*
Expand Down

0 comments on commit a5d0382

Please sign in to comment.