From 5bc6f7b3a95863d42cdc2d3e636513b6c0880513 Mon Sep 17 00:00:00 2001 From: Cristiano Tofani Date: Tue, 19 Jan 2021 13:16:34 +0100 Subject: [PATCH] feat(Carta Giovani Nazionale): [#176443044] Adds directories skeleton for CGN implementation (#2718) * [#176443044] Adds some skaffolding for CGN store, reducer and navigation * [#176443044] Minor fixes * Update ts/features/bonus/cgn/screens/onboarding/CgnInformationScreen.tsx Co-authored-by: Matteo Boschi * Update ts/features/bonus/cgn/navigation/routes.ts Co-authored-by: Matteo Boschi * Update ts/features/bonus/cgn/navigation/actions.ts Co-authored-by: fabriziofff * Update ts/features/bonus/cgn/navigation/actions.ts Co-authored-by: fabriziofff * Update ts/features/bonus/cgn/navigation/actions.ts Co-authored-by: fabriziofff * Update ts/features/bonus/cgn/navigation/actions.ts Co-authored-by: fabriziofff * [#176443044] Adds a locale for activate CTA * [#176443044] removes comment * Update locales/en/index.yml Co-authored-by: Matteo Boschi Co-authored-by: fabriziofff --- locales/en/index.yml | 3 ++ locales/it/index.yml | 3 ++ .../bonusVacanze/store/reducers/index.ts | 5 +- ts/features/bonus/bonusVacanze/utils/bonus.ts | 1 + .../onboarding/BpdInformationScreen.tsx | 1 - ts/features/bonus/cgn/api/PLACEHOLDER | 0 ts/features/bonus/cgn/navigation/actions.ts | 23 +++++++++ ts/features/bonus/cgn/navigation/navigator.ts | 20 ++++++++ ts/features/bonus/cgn/navigation/routes.ts | 14 ++++++ .../onboarding/CgnInformationScreen.tsx | 49 +++++++++++++++++++ .../bonus/cgn/store/actions/activation.ts | 13 +++++ ts/features/bonus/cgn/store/actions/index.ts | 3 ++ .../bonus/cgn/store/reducers/activation.ts | 47 ++++++++++++++++++ ts/features/bonus/cgn/store/reducers/index.ts | 12 +++++ ts/features/bonus/cgn/store/sagas/PLACEHOLDER | 0 ts/navigation/WalletNavigator.ts | 15 +++++- ts/store/actions/types.ts | 4 +- 17 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 ts/features/bonus/cgn/api/PLACEHOLDER create mode 100644 ts/features/bonus/cgn/navigation/actions.ts create mode 100644 ts/features/bonus/cgn/navigation/navigator.ts create mode 100644 ts/features/bonus/cgn/navigation/routes.ts create mode 100644 ts/features/bonus/cgn/screens/onboarding/CgnInformationScreen.tsx create mode 100644 ts/features/bonus/cgn/store/actions/activation.ts create mode 100644 ts/features/bonus/cgn/store/actions/index.ts create mode 100644 ts/features/bonus/cgn/store/reducers/activation.ts create mode 100644 ts/features/bonus/cgn/store/reducers/index.ts create mode 100644 ts/features/bonus/cgn/store/sagas/PLACEHOLDER diff --git a/locales/en/index.yml b/locales/en/index.yml index b6552fffe1f..5763cd6515b 100644 --- a/locales/en/index.yml +++ b/locales/en/index.yml @@ -2087,6 +2087,9 @@ bonus: active: "**The ranking changes every day.** The final one will be displayed on {{endDate}}." closed: "Ranking for this period is closed." lastUpdate: Updated at {{hour}} on {{date}} + cgn: + cta: + activeBonus: Activate your Carta Giovani webView: error: missingParams: Not all information necessary to access this page are available diff --git a/locales/it/index.yml b/locales/it/index.yml index 3a2657e5353..75df9a723b9 100644 --- a/locales/it/index.yml +++ b/locales/it/index.yml @@ -2118,6 +2118,9 @@ bonus: active: "**La classifica cambia ogni giorno.** Quella definitiva sarà visualizzata il {{endDate}}." closed: "La classifica per questo periodo è chiusa." lastUpdate: Aggiornato alle {{hour}} del {{date}} + cgn: + cta: + activeBonus: Attiva la tua Carta Giovani webView: error: missingParams: Non sono presenti le informazioni necessarie per accedere a questa pagina diff --git a/ts/features/bonus/bonusVacanze/store/reducers/index.ts b/ts/features/bonus/bonusVacanze/store/reducers/index.ts index 23614343049..d0078685eb1 100644 --- a/ts/features/bonus/bonusVacanze/store/reducers/index.ts +++ b/ts/features/bonus/bonusVacanze/store/reducers/index.ts @@ -1,6 +1,7 @@ import { combineReducers } from "redux"; import { Action } from "../../../../../store/actions/types"; import bpdReducer, { BpdState } from "../../../bpd/store/reducers"; +import cgnReducer, { CgnState } from "../../../cgn/store/reducers"; import bonusVacanzeActivationReducer, { ActivationState } from "./activation"; import allActiveReducer, { AllActiveState } from "./allActive"; import availableBonusesReducer, { @@ -19,6 +20,7 @@ export type BonusState = Readonly<{ availableBonusTypes: AvailableBonusTypesState; bonusVacanze: BonusVacanzeState; bpd: BpdState; + cgn: CgnState; }>; const bonusVacanzeReducer = combineReducers({ @@ -30,7 +32,8 @@ const bonusVacanzeReducer = combineReducers({ const bonusReducer = combineReducers({ availableBonusTypes: availableBonusesReducer, bonusVacanze: bonusVacanzeReducer, - bpd: bpdReducer + bpd: bpdReducer, + cgn: cgnReducer }); export default bonusReducer; diff --git a/ts/features/bonus/bonusVacanze/utils/bonus.ts b/ts/features/bonus/bonusVacanze/utils/bonus.ts index a4be202d632..86eb6cd995b 100644 --- a/ts/features/bonus/bonusVacanze/utils/bonus.ts +++ b/ts/features/bonus/bonusVacanze/utils/bonus.ts @@ -13,6 +13,7 @@ import { EligibilityRequestProgressEnum } from "../store/reducers/eligibility"; export const ID_BONUS_VACANZE_TYPE = 1; export const ID_BPD_TYPE = 2; +export const ID_CGN_TYPE = 3; // return true if the bonus is active export const isBonusActive = (bonus: BonusActivationWithQrCode) => diff --git a/ts/features/bonus/bpd/screens/onboarding/BpdInformationScreen.tsx b/ts/features/bonus/bpd/screens/onboarding/BpdInformationScreen.tsx index e51e4459dae..95ab27f926c 100644 --- a/ts/features/bonus/bpd/screens/onboarding/BpdInformationScreen.tsx +++ b/ts/features/bonus/bpd/screens/onboarding/BpdInformationScreen.tsx @@ -49,7 +49,6 @@ const BpdInformationScreen: React.FunctionComponent = (props: Props) => { }; const mapStateToProps = (state: GlobalState) => ({ - // display the error with the retry only in case of networking errors bonus: availableBonusTypesSelectorFromId(ID_BPD_TYPE)(state), bpdActiveBonus: bpdEnabledSelector(state) }); diff --git a/ts/features/bonus/cgn/api/PLACEHOLDER b/ts/features/bonus/cgn/api/PLACEHOLDER new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ts/features/bonus/cgn/navigation/actions.ts b/ts/features/bonus/cgn/navigation/actions.ts new file mode 100644 index 00000000000..7105354935e --- /dev/null +++ b/ts/features/bonus/cgn/navigation/actions.ts @@ -0,0 +1,23 @@ +import { NavigationActions } from "react-navigation"; +import CGN_ROUTES from "./routes"; + +export const navigateToCgnOnboardingLoadActivationStatus = () => + NavigationActions.navigate({ + routeName: CGN_ROUTES.ACTIVATION.LOAD_CHECK_ACTIVATION_STATUS + }); + +export const navigateToCgnOnboardingInformationTos = () => + NavigationActions.navigate({ + routeName: CGN_ROUTES.ACTIVATION.INFORMATION_TOS + }); + +export const navigateToCgnOnboardingLoadActivate = () => + NavigationActions.navigate({ + routeName: CGN_ROUTES.ACTIVATION.LOAD_ACTIVATE + }); + +// Details +export const navigateToCgnDetails = () => + NavigationActions.navigate({ + routeName: CGN_ROUTES.DETAILS + }); diff --git a/ts/features/bonus/cgn/navigation/navigator.ts b/ts/features/bonus/cgn/navigation/navigator.ts new file mode 100644 index 00000000000..600ea3def39 --- /dev/null +++ b/ts/features/bonus/cgn/navigation/navigator.ts @@ -0,0 +1,20 @@ +import { createStackNavigator } from "react-navigation"; +import CgnInformationScreen from "../screens/onboarding/CgnInformationScreen"; +import CGN_ROUTES from "./routes"; + +const CgnNavigator = createStackNavigator( + { + [CGN_ROUTES.ACTIVATION.INFORMATION_TOS]: { + screen: CgnInformationScreen + } + }, + { + // Let each screen handle the header and navigation + headerMode: "none", + defaultNavigationOptions: { + gesturesEnabled: false + } + } +); + +export default CgnNavigator; diff --git a/ts/features/bonus/cgn/navigation/routes.ts b/ts/features/bonus/cgn/navigation/routes.ts new file mode 100644 index 00000000000..9a2e174d595 --- /dev/null +++ b/ts/features/bonus/cgn/navigation/routes.ts @@ -0,0 +1,14 @@ +const CGN_ROUTES = { + MAIN: "CGN_ROUTES_MAIN", + + ACTIVATION: { + LOAD_CHECK_ACTIVATION_STATUS: "CGN_LOAD_CHECK_ACTIVATION_STATUS", + INFORMATION_TOS: "CGN_INFORMATION_TOS", + LOAD_ACTIVATE: "CGN_LOAD_ACTIVATE" + }, + + DETAILS: "CGN_DETAILS", + CTA_START_CGN: "CTA_START_CGN" +}; + +export default CGN_ROUTES; diff --git a/ts/features/bonus/cgn/screens/onboarding/CgnInformationScreen.tsx b/ts/features/bonus/cgn/screens/onboarding/CgnInformationScreen.tsx new file mode 100644 index 00000000000..b8750a23d7f --- /dev/null +++ b/ts/features/bonus/cgn/screens/onboarding/CgnInformationScreen.tsx @@ -0,0 +1,49 @@ +import * as React from "react"; +import { connect } from "react-redux"; +import I18n from "../../../../../i18n"; +import { Dispatch } from "../../../../../store/actions/types"; +import { GlobalState } from "../../../../../store/reducers/types"; +import { emptyContextualHelp } from "../../../../../utils/emptyContextualHelp"; +import { availableBonusTypesSelectorFromId } from "../../../bonusVacanze/store/reducers/availableBonusesTypes"; +import { ID_CGN_TYPE } from "../../../bonusVacanze/utils/bonus"; +import BonusInformationComponent from "../../../common/components/BonusInformationComponent"; +import { navigateBack } from "../../../../../store/actions/navigation"; + +export type Props = ReturnType & + ReturnType; + +/** + * This Screen shows all the information about the cgn program, with the rules and t&c. + */ +const CgnInformationScreen: React.FunctionComponent = (props: Props) => { + const onConfirm = () => props.userActivateCgn(); + + return ( + <> + {props.bonus ? ( + + ) : null} + + ); +}; + +const mapStateToProps = (state: GlobalState) => ({ + // display the error with the retry only in case of networking errors + bonus: availableBonusTypesSelectorFromId(ID_CGN_TYPE)(state) +}); + +const mapDispatchToProps = (dispatch: Dispatch) => ({ + userActivateCgn: () => null, + onCancel: () => dispatch(navigateBack()) +}); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(CgnInformationScreen); diff --git a/ts/features/bonus/cgn/store/actions/activation.ts b/ts/features/bonus/cgn/store/actions/activation.ts new file mode 100644 index 00000000000..aa51c53f965 --- /dev/null +++ b/ts/features/bonus/cgn/store/actions/activation.ts @@ -0,0 +1,13 @@ +import { ActionType, createAsyncAction } from "typesafe-actions"; +import { NullType } from "io-ts"; + +/** + * get and handle activation state of a CGN + */ +export const cgnActivationStatus = createAsyncAction( + "CGN_ACTIVATION_STATUS_REQUEST", + "CGN_ACTIVATION_STATUS_SUCCESS", + "CGN_ACTIVATION_STATUS_FAILURE" +)(); // Replace when API spec is correctly linked and defined + +export type CgnActivationActions = ActionType; diff --git a/ts/features/bonus/cgn/store/actions/index.ts b/ts/features/bonus/cgn/store/actions/index.ts new file mode 100644 index 00000000000..3c294db2e9c --- /dev/null +++ b/ts/features/bonus/cgn/store/actions/index.ts @@ -0,0 +1,3 @@ +import { CgnActivationActions } from "./activation"; + +export type CgnActions = CgnActivationActions; diff --git a/ts/features/bonus/cgn/store/reducers/activation.ts b/ts/features/bonus/cgn/store/reducers/activation.ts new file mode 100644 index 00000000000..20d896df365 --- /dev/null +++ b/ts/features/bonus/cgn/store/reducers/activation.ts @@ -0,0 +1,47 @@ +// bonus reducer +import { getType } from "typesafe-actions"; +import { Action } from "../../../../../store/actions/types"; +import { cgnActivationStatus } from "../actions/activation"; + +export enum CgnActivationProgressEnum { + "UNDEFINED" = "UNDEFINED", + "PROGRESS" = "PROGRESS", // The request is started + "PENDING" = "PENDING", // Polling time exceeded + "ERROR" = "ERROR", // The request is started + "EXISTS" = "EXISTS", // Another bonus related to this user was found + "SUCCESS" = "SUCCESS" // Activation has been completed +} + +export type ActivationState = { + status: CgnActivationProgressEnum; +}; + +const INITIAL_STATE: ActivationState = { + status: CgnActivationProgressEnum.UNDEFINED +}; +const reducer = ( + state: ActivationState = INITIAL_STATE, + action: Action +): ActivationState => { + switch (action.type) { + // bonus activation + case getType(cgnActivationStatus.request): + return { + ...state, + status: CgnActivationProgressEnum.PROGRESS + }; + case getType(cgnActivationStatus.success): + return { + ...state, + status: CgnActivationProgressEnum.SUCCESS // To replace with action payload when types are defined + }; + case getType(cgnActivationStatus.failure): + return { + ...state, + status: CgnActivationProgressEnum.ERROR + }; + } + return state; +}; + +export default reducer; diff --git a/ts/features/bonus/cgn/store/reducers/index.ts b/ts/features/bonus/cgn/store/reducers/index.ts new file mode 100644 index 00000000000..26a2dd7cc63 --- /dev/null +++ b/ts/features/bonus/cgn/store/reducers/index.ts @@ -0,0 +1,12 @@ +import { Action, combineReducers } from "redux"; +import cgnActivationReducer, { ActivationState } from "./activation"; + +export type CgnState = { + activation: ActivationState; +}; + +const cgnReducer = combineReducers({ + activation: cgnActivationReducer +}); + +export default cgnReducer; diff --git a/ts/features/bonus/cgn/store/sagas/PLACEHOLDER b/ts/features/bonus/cgn/store/sagas/PLACEHOLDER new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ts/navigation/WalletNavigator.ts b/ts/navigation/WalletNavigator.ts index f591245d4c5..219ce2de435 100644 --- a/ts/navigation/WalletNavigator.ts +++ b/ts/navigation/WalletNavigator.ts @@ -1,5 +1,5 @@ import { createStackNavigator } from "react-navigation"; -import { bonusVacanzeEnabled, bpdEnabled } from "../config"; +import { bonusVacanzeEnabled, bpdEnabled, cgnEnabled } from "../config"; import BonusVacanzeNavigator from "../features/bonus/bonusVacanze/navigation/navigator"; import BONUSVACANZE_ROUTES from "../features/bonus/bonusVacanze/navigation/routes"; import BpdNavigator from "../features/bonus/bpd/navigation/navigator"; @@ -33,6 +33,8 @@ import TransactionDetailsScreen from "../screens/wallet/TransactionDetailsScreen import TransactionsScreen from "../screens/wallet/TransactionsScreen"; import WalletHomeScreen from "../screens/wallet/WalletHomeScreen"; import WalletsScreen from "../screens/wallet/WalletsScreen"; +import CGN_ROUTES from "../features/bonus/cgn/navigation/routes"; +import CgnNavigator from "../features/bonus/cgn/navigation/navigator"; import ROUTES from "./routes"; const baseRouteConfigMap = { @@ -132,10 +134,19 @@ const bpdConfigMap = bpdEnabled } : {}; +const cgnConfigMap = cgnEnabled + ? { + [CGN_ROUTES.MAIN]: { + screen: CgnNavigator + } + } + : {}; + const routeConfig = { ...baseRouteConfigMap, ...bonusVacanzeConfigMap, - ...bpdConfigMap + ...bpdConfigMap, + ...cgnConfigMap }; /** diff --git a/ts/store/actions/types.ts b/ts/store/actions/types.ts index 94c7775513f..2507a3d6ec8 100644 --- a/ts/store/actions/types.ts +++ b/ts/store/actions/types.ts @@ -14,6 +14,7 @@ import { AbiActions } from "../../features/wallet/onboarding/bancomat/store/acti import { BPayActions } from "../../features/wallet/onboarding/bancomatPay/store/actions"; import { SatispayActions } from "../../features/wallet/onboarding/satispay/store/actions"; import { GlobalState } from "../reducers/types"; +import { CgnActions } from "../../features/bonus/cgn/store/actions"; import { AnalyticsActions } from "./analytics"; import { ApplicationActions } from "./application"; import { AuthenticationActions } from "./authentication"; @@ -82,7 +83,8 @@ export type Action = | BpdActions | AbiActions | BPayActions - | SatispayActions; + | SatispayActions + | CgnActions; export type Dispatch = DispatchAPI;