diff --git a/ts/features/itwallet/common/components/discoveryBanner/ItwDiscoveryBanner.tsx b/ts/features/itwallet/common/components/discoveryBanner/ItwDiscoveryBanner.tsx index 7dc01f4aee5..dff9b20da2a 100644 --- a/ts/features/itwallet/common/components/discoveryBanner/ItwDiscoveryBanner.tsx +++ b/ts/features/itwallet/common/components/discoveryBanner/ItwDiscoveryBanner.tsx @@ -11,6 +11,8 @@ import { trackITWalletBannerVisualized } from "../../../analytics"; import { ITW_ROUTES } from "../../../navigation/routes"; +import { useIODispatch } from "../../../../../store/hooks"; +import { itwCloseDiscoveryBanner } from "../../store/actions/preferences"; /** * to use in flows where we want to handle the banner's visibility logic externally @@ -31,6 +33,7 @@ export const ItwDiscoveryBanner = ({ handleOnClose }: ItwDiscoveryBannerProps) => { const bannerRef = React.createRef(); + const dispatch = useIODispatch(); const navigation = useIONavigation(); const route = useRoute(); @@ -56,6 +59,7 @@ export const ItwDiscoveryBanner = ({ const handleClose = () => { trackItWalletBannerClosure(trackBannerProperties); handleOnClose?.(); + dispatch(itwCloseDiscoveryBanner()); }; return ( diff --git a/ts/features/itwallet/common/components/discoveryBanner/ItwDiscoveryBannerStandalone.tsx b/ts/features/itwallet/common/components/discoveryBanner/ItwDiscoveryBannerStandalone.tsx index aaf4187fb0c..93290902411 100644 --- a/ts/features/itwallet/common/components/discoveryBanner/ItwDiscoveryBannerStandalone.tsx +++ b/ts/features/itwallet/common/components/discoveryBanner/ItwDiscoveryBannerStandalone.tsx @@ -1,52 +1,32 @@ -import React, { ReactElement } from "react"; -import { View } from "react-native"; +import React from "react"; +import { StyleSheet, View } from "react-native"; import { useIOSelector } from "../../../../../store/hooks"; import { isItwDiscoveryBannerRenderableSelector } from "../../store/selectors"; -import { - ItwDiscoveryBanner, - ItwDiscoveryBannerProps -} from "./ItwDiscoveryBanner"; - -type Props = Omit & { - fallbackComponent?: ReactElement; -}; +import { ItwDiscoveryBanner } from "./ItwDiscoveryBanner"; /** - * to use in flows where either - * - we need a fallback component - * - we do not want to handle the banner's visibility logic externally - * (see MultiBanner feature for the landing screen) + * to use in flows where we do not want to handle the banner's visibility logic externally + * (see MultiBanner feature for the landing screen) */ -export const ItwDiscoveryBannerStandalone = (props: Props) => { - const [isVisible, setVisible] = React.useState(true); - +export const ItwDiscoveryBannerStandalone = () => { const isBannerRenderable = useIOSelector( isItwDiscoveryBannerRenderableSelector ); - const shouldBeHidden = React.useMemo( - () => - // Banner should be hidden if: - !isVisible || // The user closed it by pressing the `x` button - !isBannerRenderable, // the various validity checks fail - [isBannerRenderable, isVisible] - ); - - if (shouldBeHidden) { - const { fallbackComponent } = props; - if (fallbackComponent) { - return fallbackComponent; - } + if (!isBannerRenderable) { return null; } return ( - - setVisible(false)} - {...props} - /> + + ); }; + +const styles = StyleSheet.create({ + wrapper: { + marginTop: 16, + marginBottom: 8 + } +}); diff --git a/ts/features/itwallet/common/components/discoveryBanner/__tests__/ItwDiscoveryBanner.test.tsx b/ts/features/itwallet/common/components/discoveryBanner/__tests__/ItwDiscoveryBanner.test.tsx index 8944c46d7c1..cc02665c16b 100644 --- a/ts/features/itwallet/common/components/discoveryBanner/__tests__/ItwDiscoveryBanner.test.tsx +++ b/ts/features/itwallet/common/components/discoveryBanner/__tests__/ItwDiscoveryBanner.test.tsx @@ -20,10 +20,6 @@ type RenderOptions = { isItwEnabled?: boolean; }; -jest.mock("../../../../../../config", () => ({ - itwEnabled: true -})); - describe("ItwDiscoveryBanner", () => { const globalState = appReducer(undefined, applicationChangeState("active")); const component = renderScreenWithNavigationStoreContext( diff --git a/ts/features/itwallet/common/store/actions/preferences.ts b/ts/features/itwallet/common/store/actions/preferences.ts index 591905d0a99..b89cf8cffbf 100644 --- a/ts/features/itwallet/common/store/actions/preferences.ts +++ b/ts/features/itwallet/common/store/actions/preferences.ts @@ -4,4 +4,10 @@ export const itwCloseFeedbackBanner = createStandardAction( "ITW_CLOSE_FEEDBACK_BANNER" )(); -export type ItwPreferencesActions = ActionType; +export const itwCloseDiscoveryBanner = createStandardAction( + "ITW_CLOSE_DISCOVERY_BANNER" +)(); + +export type ItwPreferencesActions = + | ActionType + | ActionType; diff --git a/ts/features/itwallet/common/store/reducers/preferences.ts b/ts/features/itwallet/common/store/reducers/preferences.ts index 73eead742e3..691872e4be1 100644 --- a/ts/features/itwallet/common/store/reducers/preferences.ts +++ b/ts/features/itwallet/common/store/reducers/preferences.ts @@ -1,10 +1,14 @@ import { addMonths } from "date-fns"; import { getType } from "typesafe-actions"; import { Action } from "../../../../../store/actions/types"; -import { itwCloseFeedbackBanner } from "../actions/preferences"; +import { + itwCloseDiscoveryBanner, + itwCloseFeedbackBanner +} from "../actions/preferences"; export type ItwPreferencesState = { hideFeedbackBannerUntilDate?: string; + hideDiscoveryBannerUntilDate?: string; }; const INITIAL_STATE: ItwPreferencesState = {}; @@ -16,10 +20,18 @@ const reducer = ( switch (action.type) { case getType(itwCloseFeedbackBanner): { return { + ...state, hideFeedbackBannerUntilDate: addMonths(new Date(), 1).toISOString() }; } + case getType(itwCloseDiscoveryBanner): { + return { + ...state, + hideDiscoveryBannerUntilDate: addMonths(new Date(), 6).toISOString() + }; + } + default: return state; } diff --git a/ts/features/itwallet/common/store/selectors/__tests__/preferences.test.ts b/ts/features/itwallet/common/store/selectors/__tests__/preferences.test.ts index 0ee9875f6d1..0a4e9d6b1c8 100644 --- a/ts/features/itwallet/common/store/selectors/__tests__/preferences.test.ts +++ b/ts/features/itwallet/common/store/selectors/__tests__/preferences.test.ts @@ -2,7 +2,10 @@ import { addDays, addMonths } from "date-fns"; import _ from "lodash"; import { applicationChangeState } from "../../../../../../store/actions/application"; import { appReducer } from "../../../../../../store/reducers"; -import { itwIsFeedbackBannerHiddenSelector } from "../preferences"; +import { + itwIsDiscoveryBannerHiddenSelector, + itwIsFeedbackBannerHiddenSelector +} from "../preferences"; describe("itwIsFeedbackBannerHiddenSelector", () => { it.each([ @@ -24,3 +27,23 @@ describe("itwIsFeedbackBannerHiddenSelector", () => { ).toBe(expected); }); }); + +describe("itwIsDiscoveryBannerHiddenSelector", () => { + it.each([ + [false, undefined], + [false, "definitely not a date"], + [false, new Date().toISOString()], + [false, addDays(new Date(), -2).toISOString()], + [true, addMonths(new Date(), 2).toISOString()] + ])("should return %p if banner is hidden until %p", (expected, value) => { + const globalState = appReducer(undefined, applicationChangeState("active")); + + expect( + itwIsDiscoveryBannerHiddenSelector( + _.set(globalState, "features.itWallet.preferences", { + hideDiscoveryBannerUntilDate: value + }) + ) + ).toBe(expected); + }); +}); diff --git a/ts/features/itwallet/common/store/selectors/index.ts b/ts/features/itwallet/common/store/selectors/index.ts index 560bb92f214..a6e6cf65552 100644 --- a/ts/features/itwallet/common/store/selectors/index.ts +++ b/ts/features/itwallet/common/store/selectors/index.ts @@ -5,7 +5,10 @@ import { import { GlobalState } from "../../../../../store/reducers/types"; import { itwIsWalletEmptySelector } from "../../../credentials/store/selectors"; import { itwLifecycleIsValidSelector } from "../../../lifecycle/store/selectors"; -import { itwIsFeedbackBannerHiddenSelector } from "./preferences"; +import { + itwIsFeedbackBannerHiddenSelector, + itwIsDiscoveryBannerHiddenSelector +} from "./preferences"; /** * Returns if the discovery banner should be rendered. The banner is rendered if: @@ -17,6 +20,16 @@ import { itwIsFeedbackBannerHiddenSelector } from "./preferences"; export const isItwDiscoveryBannerRenderableSelector = (state: GlobalState) => !itwLifecycleIsValidSelector(state) && isItwEnabledSelector(state); +/** + * Returns the renderable state of the discovery banner with the persisted user's preference: + * after being closed by the user it should stay hidden for 6 months. + */ +export const isItwPersistedDiscoveryBannerRenderableSelector = ( + state: GlobalState +) => + !itwIsDiscoveryBannerHiddenSelector(state) && + isItwDiscoveryBannerRenderableSelector(state); + /** * Returns if the feedback banner should be visible. The banner is visible if: * - The Wallet has valid Wallet Instance and a valid eID diff --git a/ts/features/itwallet/common/store/selectors/preferences.ts b/ts/features/itwallet/common/store/selectors/preferences.ts index af73bc24888..51b8bc52370 100644 --- a/ts/features/itwallet/common/store/selectors/preferences.ts +++ b/ts/features/itwallet/common/store/selectors/preferences.ts @@ -3,6 +3,14 @@ import { createSelector } from "reselect"; import { GlobalState } from "../../../../../store/reducers/types"; import { ItwPreferencesState } from "../reducers/preferences"; +const isPastDate = (date?: string) => { + if (!date) { + return false; + } + const hideUntilDate = new Date(date); + return !isNaN(hideUntilDate.getTime()) && !isPast(hideUntilDate); +}; + export const itwPreferencesSelector = (state: GlobalState) => state.features.itWallet.preferences; @@ -13,13 +21,12 @@ export const itwPreferencesSelector = (state: GlobalState) => */ export const itwIsFeedbackBannerHiddenSelector = createSelector( itwPreferencesSelector, - ({ hideFeedbackBannerUntilDate }: ItwPreferencesState) => { - if (!hideFeedbackBannerUntilDate) { - return false; - } - - const hideUntilDate = new Date(hideFeedbackBannerUntilDate); + ({ hideFeedbackBannerUntilDate }: ItwPreferencesState) => + isPastDate(hideFeedbackBannerUntilDate) +); - return !isNaN(hideUntilDate.getTime()) && !isPast(hideUntilDate); - } +export const itwIsDiscoveryBannerHiddenSelector = createSelector( + itwPreferencesSelector, + ({ hideDiscoveryBannerUntilDate }: ItwPreferencesState) => + isPastDate(hideDiscoveryBannerUntilDate) ); diff --git a/ts/features/landingScreenMultiBanner/utils/landingScreenBannerMap.tsx b/ts/features/landingScreenMultiBanner/utils/landingScreenBannerMap.tsx index 2b0605d7f09..51643b7c9b8 100644 --- a/ts/features/landingScreenMultiBanner/utils/landingScreenBannerMap.tsx +++ b/ts/features/landingScreenMultiBanner/utils/landingScreenBannerMap.tsx @@ -2,7 +2,7 @@ import * as React from "react"; import { SettingsDiscoveryBanner } from "../../../screens/profile/components/SettingsDiscoveryBanner"; import { GlobalState } from "../../../store/reducers/types"; import { ItwDiscoveryBanner } from "../../itwallet/common/components/discoveryBanner/ItwDiscoveryBanner"; -import { isItwDiscoveryBannerRenderableSelector } from "../../itwallet/common/store/selectors"; +import { isItwPersistedDiscoveryBannerRenderableSelector } from "../../itwallet/common/store/selectors"; import { hasUserAcknowledgedSettingsBannerSelector } from "../../profileSettings/store/selectors"; import { PushNotificationsBanner } from "../../pushNotifications/components/PushNotificationsBanner"; import { isPushNotificationsBannerRenderableSelector } from "../../pushNotifications/store/selectors"; @@ -38,7 +38,7 @@ export const landingScreenBannerMap: BannerMapById = { component: closeHandler => ( ), - isRenderableSelector: isItwDiscoveryBannerRenderableSelector + isRenderableSelector: isItwPersistedDiscoveryBannerRenderableSelector }, SETTINGS_DISCOVERY: { component: closeHandler => ( diff --git a/ts/features/wallet/components/WalletCardsContainer.tsx b/ts/features/wallet/components/WalletCardsContainer.tsx index a4f2578c3d0..863f04efb3f 100644 --- a/ts/features/wallet/components/WalletCardsContainer.tsx +++ b/ts/features/wallet/components/WalletCardsContainer.tsx @@ -65,7 +65,7 @@ const WalletCardsContainer = () => { // the wallet is not in a loading state anymore return ( - + ); @@ -80,7 +80,7 @@ const WalletCardsContainer = () => { layout={LinearTransition.duration(200)} > - + {shouldRender("itw") && } {shouldRender("other") && }