Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Bonus Pagamenti Digitali): [#176559662] Navigation & screen stubs for co-badge workflow #2750

Merged
merged 9 commits into from
Jan 27, 2021
11 changes: 11 additions & 0 deletions locales/en/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,17 @@ wallet:
koNotFound:
title: "Sorry, we do not know that you have activated Satispay yet"
body: "Contact Satispay for activation"
coBadge:
headerTitle: "Add international circuit"
start:
title: "TMP Start Title"
search:
koTimeout:
title: "TMP KO Timeout add"
koNotFound:
title: "TMP KO Timeout add"
add:
title: "TMP Add title"
alert:
supportedCardPageLinkError: An error occurred while opening the supported cards page.
msgErrorUpdateApp: "An error occurred while opening the app store"
Expand Down
11 changes: 11 additions & 0 deletions locales/it/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,17 @@ wallet:
koNotFound:
title: "Spiacenti, non ci risulta che tu abbia ancora attivato Satispay"
body: "Contatta Satispay per l’attivazione"
coBadge:
headerTitle: "Aggiungi circuito internazionale"
start:
title: "TMP Start Title"
search:
koTimeout:
title: "TMP KO Timeout add"
koNotFound:
title: "TMP KO Timeout add"
add:
title: "TMP Add title"
alert:
supportedCardPageLinkError: Si è verificato un errore durante l'apertura della pagina di riferimento per le carte supportate.
msgErrorUpdateApp: "Si è verificato un errore durante l'apertura dello store delle app"
Expand Down
22 changes: 22 additions & 0 deletions ts/features/wallet/onboarding/cobadge/navigation/action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { NavigationActions } from "react-navigation";
import WALLET_ONBOARDING_COBADGE_ROUTES from "./routes";

export const navigateToOnboardingCoBadgeSearchStartScreen = () =>
NavigationActions.navigate({
routeName: WALLET_ONBOARDING_COBADGE_ROUTES.START
});

export const navigateToOnboardingCoBadgeSearchAvailable = () =>
NavigationActions.navigate({
routeName: WALLET_ONBOARDING_COBADGE_ROUTES.SEARCH_AVAILABLE
});

export const navigateToOnboardingCoBadgeAdd = () =>
NavigationActions.navigate({
routeName: WALLET_ONBOARDING_COBADGE_ROUTES.ADD_COBADGE
});

export const navigateToActivateBpdOnNewCoBadge = () =>
NavigationActions.navigate({
routeName: WALLET_ONBOARDING_COBADGE_ROUTES.ACTIVATE_BPD_NEW
});
32 changes: 32 additions & 0 deletions ts/features/wallet/onboarding/cobadge/navigation/navigator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { createStackNavigator } from "react-navigation";
import ActivateBpdOnNewCoBadgeScreen from "../screens/ActivateBpdOnNewCoBadgeScreen";
import AddCoBadgeScreen from "../screens/add-account/AddCoBadgeScreen";
import SearchAvailableCoBadgeScreen from "../screens/search/SearchAvailableCoBadgeScreen";
import CoBadgeStartSingleBankScreen from "../screens/start/CoBadgeStartSingleBankScreen";
import WALLET_ONBOARDING_COBADGE_ROUTES from "./routes";

const PaymentMethodOnboardingCoBadgeNavigator = createStackNavigator(
{
[WALLET_ONBOARDING_COBADGE_ROUTES.START]: {
screen: CoBadgeStartSingleBankScreen
},
[WALLET_ONBOARDING_COBADGE_ROUTES.SEARCH_AVAILABLE]: {
screen: SearchAvailableCoBadgeScreen
},
[WALLET_ONBOARDING_COBADGE_ROUTES.ADD_COBADGE]: {
screen: AddCoBadgeScreen
},
[WALLET_ONBOARDING_COBADGE_ROUTES.ACTIVATE_BPD_NEW]: {
screen: ActivateBpdOnNewCoBadgeScreen
}
},
{
// Let each screen handle the header and navigation
headerMode: "none",
defaultNavigationOptions: {
gesturesEnabled: false
}
}
);

export default PaymentMethodOnboardingCoBadgeNavigator;
11 changes: 11 additions & 0 deletions ts/features/wallet/onboarding/cobadge/navigation/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const WALLET_ONBOARDING_COBADGE_ROUTES = {
MAIN: "WALLET_ONBOARDING_COBADGE_MAIN",

START: "WALLET_ONBOARDING_COBADGE_START",
SEARCH_AVAILABLE: "WALLET_ONBOARDING_COBADGE_SEARCH_AVAILABLE",

ADD_COBADGE: "WALLET_ONBOARDING_COBADGE_ADD",
ACTIVATE_BPD_NEW: "WALLET_ONBOARDING_COBADGE_ACTIVATE_BPD_NEW"
};

export default WALLET_ONBOARDING_COBADGE_ROUTES;
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as React from "react";
import { connect } from "react-redux";
import I18n from "../../../../../i18n";
import { GlobalState } from "../../../../../store/reducers/types";
import { emptyContextualHelp } from "../../../../../utils/emptyContextualHelp";
import ActivateBpdOnNewPaymentMethodScreen from "../../common/screens/bpd/ActivateBpdOnNewPaymentMethodScreen";
import { onboardingCoBadgeAddedSelector } from "../store/reducers/addedCoBadge";

type Props = ReturnType<typeof mapStateToProps>;

/**
* The user can activate the cashback on the new added coBadge card
* @param props
* @constructor
*/
const ActivateBpdOnNewCoBadgeScreen = (props: Props) => (
<ActivateBpdOnNewPaymentMethodScreen
paymentMethods={props.newCoBadge}
title={I18n.t("wallet.onboarding.coBadge.headerTitle")}
contextualHelp={emptyContextualHelp}
/>
);

const mapStateToProps = (state: GlobalState) => ({
newCoBadge: onboardingCoBadgeAddedSelector(state)
});
export default connect(mapStateToProps)(ActivateBpdOnNewCoBadgeScreen);
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { index } from "fp-ts/lib/Array";
import { fromNullable } from "fp-ts/lib/Option";
import * as pot from "italia-ts-commons/lib/pot";
import { Button } from "native-base";
import * as React from "react";
import { SafeAreaView } from "react-native";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import { H1 } from "../../../../../../components/core/typography/H1";
import BaseScreenComponent from "../../../../../../components/screens/BaseScreenComponent";
import { profileSelector } from "../../../../../../store/reducers/profile";
import { GlobalState } from "../../../../../../store/reducers/types";
import {
getValueOrElse,
isError,
isLoading,
isReady
} from "../../../../../bonus/bpd/model/RemoteValue";
import {
addCoBadgeToWallet,
CoBadgeResponse,
walletAddCoBadgeCancel,
walletAddCoBadgeCompleted
} from "../../store/actions";
import {
onboardingCobadgeAddingResultSelector,
onboardingCobadgeChosenPanSelector
} from "../../store/reducers/addingCoBadge";
import { onboardingCoBadgeFoundSelector } from "../../store/reducers/foundCoBadge";
import LoadAddCoBadgeComponent from "./LoadAddCoBadgeComponent";

type Props = ReturnType<typeof mapStateToProps> &
ReturnType<typeof mapDispatchToProps> &
Pick<React.ComponentProps<typeof BaseScreenComponent>, "contextualHelp">;

type NextAction = {
index: number;
skip: boolean;
};
/**
* This screen is displayed when co-badge cards are found and ready to be added in wallet
* @constructor
*/
const AddCoBadgeScreen = (props: Props): React.ReactElement | null => {
// next could be skip or not (a pan should be added)
const [currentAction, setNextAction] = React.useState<NextAction>({
index: 0,
skip: false
});

const currentIndex = currentAction.index;

React.useEffect(() => {
// call onCompleted when the end of co-badge list has been reached
// and the adding phase has been completed (or it was skipped step)
if (
currentIndex >= props.coBadgeList.length &&
(currentAction.skip || props.isAddingReady)
) {
props.onCompleted();
}
}, [currentAction, props.isAddingReady]);

const nextPan = (skip: boolean) => {
const nextIndex = currentIndex + 1;
setNextAction({ index: nextIndex, skip });
};

const handleOnContinue = () => {
if (currentIndex < props.coBadgeList.length) {
props.addCoBadge(props.coBadgeList[currentIndex]);
}
nextPan(false);
};

const currentPan = index(currentIndex, [...props.coBadgeList]);

return props.loading || props.isAddingResultError ? (
<LoadAddCoBadgeComponent
isLoading={!props.isAddingResultError}
onCancel={props.onCancel}
onRetry={() => fromNullable(props.selectedCoBadge).map(props.onRetry)}
/>
) : currentPan.isSome() ? (
// TODO replace with iterative component
<SafeAreaView>
<Button onPress={handleOnContinue}>
<H1>TMP Add</H1>
</Button>
</SafeAreaView>
) : null; // this should not happen
};

const mapDispatchToProps = (dispatch: Dispatch) => ({
addCoBadge: (coBadge: CoBadgeResponse) =>
dispatch(addCoBadgeToWallet.request(coBadge)),
onCompleted: () => dispatch(walletAddCoBadgeCompleted()),
onCancel: () => dispatch(walletAddCoBadgeCancel()),
onRetry: (coBadge: CoBadgeResponse) =>
dispatch(addCoBadgeToWallet.request(coBadge))
});

const mapStateToProps = (state: GlobalState) => {
const remoteCoBadge = onboardingCoBadgeFoundSelector(state);
const addingResult = onboardingCobadgeAddingResultSelector(state);
const coBadgeList = getValueOrElse(remoteCoBadge, []);
return {
isAddingReady: isReady(addingResult),
loading: isLoading(addingResult),
isAddingResultError: isError(addingResult),
remoteCoBadge,
selectedCoBadge: onboardingCobadgeChosenPanSelector(state),
coBadgeList,
profile: pot.toUndefined(profileSelector(state))
};
};

export default connect(mapStateToProps, mapDispatchToProps)(AddCoBadgeScreen);
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as React from "react";
import { useHardwareBackButton } from "../../../../../bonus/bonusVacanze/components/hooks/useHardwareBackButton";
import { LoadingErrorComponent } from "../../../../../bonus/bonusVacanze/components/loadingErrorScreen/LoadingErrorComponent";

export type Props = {
isLoading: boolean;
onCancel: () => void;
onRetry: () => void;
};

/**
* This screen displays a loading or error message while adding a co-badge card to the wallet
* In error case a retry button will be shown
* @constructor
*/
const LoadAddCoBadgeComponent = (props: Props): React.ReactElement => {
useHardwareBackButton(() => {
if (!props.isLoading) {
props.onCancel();
}
return true;
});
return (
<LoadingErrorComponent
{...props}
// TODO: add caption
loadingCaption={"TMP Load"}
onAbort={props.onCancel}
onRetry={props.onRetry}
/>
);
};

export default LoadAddCoBadgeComponent;
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as React from "react";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import { GlobalState } from "../../../../../../store/reducers/types";
import { useHardwareBackButton } from "../../../../../bonus/bonusVacanze/components/hooks/useHardwareBackButton";
import { LoadingErrorComponent } from "../../../../../bonus/bonusVacanze/components/loadingErrorScreen/LoadingErrorComponent";
import { searchUserCoBadge, walletAddCoBadgeCancel } from "../../store/actions";
import { onboardingCoBadgeAbiSelectedSelector } from "../../store/reducers/abiSelected";
import { onboardingCoBadgeFoundSelector } from "../../store/reducers/foundCoBadge";

export type Props = ReturnType<typeof mapDispatchToProps> &
ReturnType<typeof mapStateToProps>;

/**
* This screen is displayed when searching for co-badge
* @constructor
*/
const LoadCoBadgeSearch = (props: Props): React.ReactElement => {
useHardwareBackButton(() => {
if (!props.isLoading) {
props.cancel();
}
return true;
});
return (
<LoadingErrorComponent
{...props}
// TODO: replace with localeso
loadingCaption={"TMP Loading"}
onAbort={props.cancel}
onRetry={() => props.retry(props.abiSelected)}
/>
);
};

const mapDispatchToProps = (dispatch: Dispatch) => ({
cancel: () => dispatch(walletAddCoBadgeCancel()),
retry: (abiSelected: string | undefined) =>
dispatch(searchUserCoBadge.request(abiSelected))
});

const mapStateToProps = (state: GlobalState) => ({
abiSelected: onboardingCoBadgeAbiSelectedSelector(state),
isLoading: !onboardingCoBadgeFoundSelector(state)
});

export default connect(mapStateToProps, mapDispatchToProps)(LoadCoBadgeSearch);
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as React from "react";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import { GlobalState } from "../../../../../../store/reducers/types";
import { emptyContextualHelp } from "../../../../../../utils/emptyContextualHelp";
import { isTimeoutError } from "../../../../../../utils/errors";
import {
isError,
isLoading,
isReady
} from "../../../../../bonus/bpd/model/RemoteValue";
import { onboardingCoBadgeFoundSelector } from "../../store/reducers/foundCoBadge";
import AddCoBadgeScreen from "../add-account/AddCoBadgeScreen";
import CoBadgeKoNotFound from "./ko/CoBadgeKoNotFound";
import CoBadgeKoTimeout from "./ko/CoBadgeKoTimeout";
import LoadCoBadgeSearch from "./LoadCoBadgeSearch";

export type Props = ReturnType<typeof mapDispatchToProps> &
ReturnType<typeof mapStateToProps>;

/**
* This screen handle the errors and loading for the co-badge.
* @constructor
*/
const SearchAvailableCoBadgeScreen = (props: Props): React.ReactElement => {
const coBadgeFound = props.coBadgeFound;
const noCoBadgeFound =
isReady(coBadgeFound) && coBadgeFound.value.length === 0;

if (noCoBadgeFound) {
// The user doesn't have a co-badge cards
return <CoBadgeKoNotFound contextualHelp={emptyContextualHelp} />;
}

if (isError(coBadgeFound) && isTimeoutError(coBadgeFound.error)) {
return <CoBadgeKoTimeout contextualHelp={emptyContextualHelp} />;
}
if (isLoading(coBadgeFound) || isError(coBadgeFound)) {
return <LoadCoBadgeSearch />;
}

// TODO: add CoBadgeKoSingleBankNotFound and CoBadgeKoServicesError

// success! The user can now optionally add found co-badge cards to the wallet
return <AddCoBadgeScreen contextualHelp={emptyContextualHelp} />;
};

const mapDispatchToProps = (_: Dispatch) => ({});

const mapStateToProps = (state: GlobalState) => ({
coBadgeFound: onboardingCoBadgeFoundSelector(state)
});

export default connect(
mapStateToProps,
mapDispatchToProps
)(SearchAvailableCoBadgeScreen);
Loading