diff --git a/ts/components/cta/CTAsBar.tsx b/ts/components/cta/CTAsBar.tsx
deleted file mode 100644
index 2ae53aba35b..00000000000
--- a/ts/components/cta/CTAsBar.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import React, { useCallback } from "react";
-import { View } from "react-native";
-import { useLinkTo } from "@react-navigation/native";
-import {
- ButtonOutline,
- ButtonSolid,
- HSpacer,
- IOStyles
-} from "@pagopa/io-app-design-system";
-import { CTA, CTAS } from "../../features/messages/types/MessageCTA";
-import { ServiceId } from "../../../definitions/backend/ServiceId";
-import { trackPNOptInMessageAccepted } from "../../features/pn/analytics";
-import { handleCtaAction } from "../../features/messages/utils/messages";
-import { usePNOptInMessage } from "../../features/pn/hooks/usePNOptInMessage";
-
-type CTAsBarProps = {
- ctas: CTAS;
- serviceId: ServiceId;
-};
-
-/**
- * render cta_1 and cta_2 if they are defined in the message content as front-matter
- * or if they are defined on cta attribute in ServiceMetadata in the ServiceDetailScreen
- */
-export const CTAsBar = ({ ctas, serviceId }: CTAsBarProps) => {
- const { cta_1, cta_2 } = ctas;
- const {
- cta1HasServiceNavigationLink,
- cta2HasServiceNavigationLink,
- isPNOptInMessage
- } = usePNOptInMessage(ctas, serviceId);
-
- const linkTo = useLinkTo();
-
- const handleOnPress = useCallback(
- (cta: CTA, isServiceNavigationLink: boolean) => {
- if (isPNOptInMessage && isServiceNavigationLink) {
- trackPNOptInMessageAccepted();
- }
- handleCtaAction(cta, linkTo, serviceId);
- },
- [isPNOptInMessage, linkTo, serviceId]
- );
-
- return (
-
- {cta_2 && (
- <>
-
- handleOnPress(cta_2, cta2HasServiceNavigationLink)}
- />
-
-
- >
- )}
-
- handleOnPress(cta_1, cta1HasServiceNavigationLink)}
- />
-
-
- );
-};
diff --git a/ts/components/cta/ExtractedCTABar.tsx b/ts/components/cta/ExtractedCTABar.tsx
index 52853f482eb..1e9bb07f129 100644
--- a/ts/components/cta/ExtractedCTABar.tsx
+++ b/ts/components/cta/ExtractedCTABar.tsx
@@ -68,7 +68,7 @@ const ExtractedCTABar: React.FunctionComponent = (
props,
linkTo,
false,
- props.isPNOptInMessage?.cta2HasServiceNavigationLink ?? false,
+ props.isPNOptInMessage?.cta2LinksToPNService ?? false,
ctas.cta_2
),
[ctas.cta_2, linkTo, props]
@@ -79,7 +79,7 @@ const ExtractedCTABar: React.FunctionComponent = (
props,
linkTo,
true,
- props.isPNOptInMessage?.cta1HasServiceNavigationLink ?? false,
+ props.isPNOptInMessage?.cta1LinksToPNService ?? false,
ctas.cta_1
),
[ctas.cta_1, linkTo, props]
diff --git a/ts/components/cta/__test__/CTAsBar.test.tsx b/ts/components/cta/__test__/CTAsBar.test.tsx
deleted file mode 100644
index d495bc76c4b..00000000000
--- a/ts/components/cta/__test__/CTAsBar.test.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import * as React from "react";
-import { createStore } from "redux";
-import { ServiceId } from "../../../../definitions/backend/ServiceId";
-import { CTAS } from "../../../features/messages/types/MessageCTA";
-import { appReducer } from "../../../store/reducers";
-import { applicationChangeState } from "../../../store/actions/application";
-import { renderScreenWithNavigationStoreContext } from "../../../utils/testWrapper";
-import { CTAsBar } from "../CTAsBar";
-
-describe("CTAsBar", () => {
- it("should match snapshot with one CTA", () => {
- const serviceId = "01HRW50F08QYXNP518JYCKVSHP" as ServiceId;
- const ctas = { cta_1: { text: "My CTA 1", action: "" } } as CTAS;
- const component = renderComponent(serviceId, ctas);
- expect(component.toJSON()).toMatchSnapshot();
- });
- it("should match snapshot with both CTAs", () => {
- const serviceId = "01HRW50F08QYXNP518JYCKVSHP" as ServiceId;
- const ctas = {
- cta_1: { text: "My CTA 1", action: "" },
- cta_2: { text: "My CTA 2", action: "" }
- } as CTAS;
- const component = renderComponent(serviceId, ctas);
- expect(component.toJSON()).toMatchSnapshot();
- });
-});
-
-const renderComponent = (serviceId: ServiceId, ctas: CTAS) => {
- const globalState = appReducer(undefined, applicationChangeState("active"));
- const store = createStore(appReducer, globalState as any);
-
- return renderScreenWithNavigationStoreContext(
- () => ,
- "DUMMY",
- {},
- store
- );
-};
diff --git a/ts/features/messages/components/MessageDetail/MessageDetailsPaymentButton.tsx b/ts/features/messages/components/MessageDetail/MessageDetailsPaymentButton.tsx
new file mode 100644
index 00000000000..09098d972ce
--- /dev/null
+++ b/ts/features/messages/components/MessageDetail/MessageDetailsPaymentButton.tsx
@@ -0,0 +1,46 @@
+import React from "react";
+import { ButtonSolid, useIOToast } from "@pagopa/io-app-design-system";
+import { PaymentData, UIMessageId } from "../../types";
+import { useIODispatch } from "../../../../store/hooks";
+import I18n from "../../../../i18n";
+import {
+ getRptIdStringFromPaymentData,
+ initializeAndNavigateToWalletForPayment
+} from "../../utils";
+
+type MessageDetailsPaymentButtonProps = {
+ messageId: UIMessageId;
+ paymentData: PaymentData;
+ canNavigateToPayment: boolean;
+ isLoading: boolean;
+};
+
+export const MessageDetailsPaymentButton = ({
+ messageId,
+ paymentData,
+ canNavigateToPayment,
+ isLoading
+}: MessageDetailsPaymentButtonProps) => {
+ const dispatch = useIODispatch();
+ const toast = useIOToast();
+ return (
+
+ initializeAndNavigateToWalletForPayment(
+ messageId,
+ getRptIdStringFromPaymentData(paymentData),
+ false,
+ paymentData.amount,
+ canNavigateToPayment,
+ dispatch,
+ false,
+ () => toast.error(I18n.t("genericError"))
+ )
+ }
+ fullWidth
+ loading={isLoading}
+ />
+ );
+};
diff --git a/ts/features/messages/components/MessageDetail/MessageDetailsScrollViewAdditionalSpace.tsx b/ts/features/messages/components/MessageDetail/MessageDetailsScrollViewAdditionalSpace.tsx
index faf56918188..dba6e46b300 100644
--- a/ts/features/messages/components/MessageDetail/MessageDetailsScrollViewAdditionalSpace.tsx
+++ b/ts/features/messages/components/MessageDetail/MessageDetailsScrollViewAdditionalSpace.tsx
@@ -9,25 +9,29 @@ import { gapBetweenItemsInAGrid } from "../../utils";
type ScrollViewAdditionalSpaceProps = {
messageId: UIMessageId;
- hasCTAS: boolean;
+ hasCTA1: boolean;
+ hasCTA2: boolean;
};
export const MessageDetailsScrollViewAdditionalSpace = ({
messageId,
- hasCTAS
+ hasCTA1,
+ hasCTA2
}: ScrollViewAdditionalSpaceProps) => {
const safeAreaInsets = useSafeAreaInsets();
const isShowingPaymentButton = useIOSelector(state =>
isPaymentsButtonVisibleSelector(state, messageId)
);
- const stickyFooterRowHeight =
- IOStyles.footer.paddingBottom + buttonSolidHeight + gapBetweenItemsInAGrid;
+ const hasAtLeastAButton = isShowingPaymentButton || hasCTA1 || hasCTA2;
const height =
- safeAreaInsets.bottom +
+ (hasAtLeastAButton ? IOStyles.footer.paddingBottom : 0) +
+ (isShowingPaymentButton ? buttonSolidHeight + gapBetweenItemsInAGrid : 0) +
+ (hasCTA1 ? buttonSolidHeight + gapBetweenItemsInAGrid : 0) +
+ (hasCTA2 ? buttonSolidHeight + gapBetweenItemsInAGrid : 0) +
+ gapBetweenItemsInAGrid +
IOStyles.footer.paddingBottom +
- (isShowingPaymentButton ? stickyFooterRowHeight : 0) +
- (hasCTAS ? stickyFooterRowHeight : 0);
+ safeAreaInsets.bottom;
return (
+ footerData.tag === "None";
+
+const foldFooterData = (
+ footerData: FooterData,
+ onPaymentWithDoubleCTA: (
+ paymentWithDoubleCTA: FooterPaymentWithDoubleCTA
+ ) => JSX.Element,
+ onPaymentWithCTA: (paymentWithCTA: FooterPaymentWithCTA) => JSX.Element,
+ onDoubleCTA: (doubleCTA: FooterDoubleCTA) => JSX.Element,
+ onPayment: (paymentCTA: FooterPayment) => JSX.Element,
+ onCTA: (cta: FooterCTA) => JSX.Element,
+ onNone: () => JSX.Element | null
+) => {
+ switch (footerData.tag) {
+ case "PaymentWithDoubleCTA":
+ return onPaymentWithDoubleCTA(footerData);
+ case "PaymentWithCTA":
+ return onPaymentWithCTA(footerData);
+ case "DoubleCTA":
+ return onDoubleCTA(footerData);
+ case "Payment":
+ return onPayment(footerData);
+ case "CTA":
+ return onCTA(footerData);
+ }
+ return onNone();
+};
+
+const computeFooterData = (
+ paymentData: PaymentData | undefined,
+ paymentButtonStatus: "hidden" | "loading" | "enabled",
+ ctas: CTAS | undefined
+): FooterData => {
+ const isPaymentButtonVisible =
+ paymentData && paymentButtonStatus !== "hidden";
+ const isCTA1Visible = !!ctas?.cta_1;
+ const cta2 = ctas?.cta_2;
+ const isCTA2Visible = !!cta2;
+ if (isPaymentButtonVisible && isCTA1Visible && isCTA2Visible) {
+ return {
+ tag: "PaymentWithDoubleCTA",
+ cta1: ctas.cta_1,
+ cta2,
+ paymentData
+ };
+ } else if (isPaymentButtonVisible && isCTA1Visible) {
+ return {
+ tag: "PaymentWithCTA",
+ cta1: ctas.cta_1,
+ paymentData
+ };
+ } else if (isCTA1Visible && isCTA2Visible) {
+ return {
+ tag: "DoubleCTA",
+ cta1: ctas.cta_1,
+ cta2
+ };
+ } else if (isPaymentButtonVisible) {
+ return {
+ tag: "Payment",
+ paymentData
+ };
+ } else if (isCTA1Visible) {
+ return {
+ tag: "CTA",
+ cta1: ctas.cta_1
+ };
+ }
+ return { tag: "None" };
+};
+
+const renderPaymentWithDoubleCTA = (
+ messageId: UIMessageId,
+ paymentData: PaymentData,
+ canNavigateToPayment: boolean,
+ isLoadingPayment: boolean,
+ cta1: CTA,
+ cta1IsPNOptInMessage: boolean,
+ cta2: CTA,
+ cta2IsPNOptInMessage: boolean,
+ onCTAPress: (cta: CTA, isPNOptInMessage: boolean) => void
+) => (
+ <>
+
+
+ onCTAPress(cta1, cta1IsPNOptInMessage)}
+ />
+
+
+ onCTAPress(cta2, cta2IsPNOptInMessage)}
+ />
+
+ >
+);
+const renderPaymentWithCTA = (
+ messageId: UIMessageId,
+ paymentData: PaymentData,
+ canNavigateToPayment: boolean,
+ isLoadingPayment: boolean,
+ cta1: CTA,
+ cta1IsPNOptInMessage: boolean,
+ onCTAPress: (cta: CTA, isPNOptInMessage: boolean) => void
+) => (
+ <>
+
+
+
+ onCTAPress(cta1, cta1IsPNOptInMessage)}
+ />
+
+ >
+);
+const renderDoubleCTA = (
+ cta1: CTA,
+ cta1IsPNOptInMessage: boolean,
+ cta2: CTA,
+ cta2IsPNOptInMessage: boolean,
+ onCTAPress: (cta: CTA, isPNOptInMessage: boolean) => void
+) => (
+ <>
+ onCTAPress(cta1, cta1IsPNOptInMessage)}
+ />
+
+
+ onCTAPress(cta2, cta2IsPNOptInMessage)}
+ />
+
+ >
+);
+const renderPayment = (
+ messageId: UIMessageId,
+ paymentData: PaymentData,
+ canNavigateToPayment: boolean,
+ isLoadingPayment: boolean
+) => (
+
+);
+const renderCTA = (
+ cta: CTA,
+ isPNOptInMessage: boolean,
+ onCTAPress: (cta: CTA, isPNOptInMessage: boolean) => void
+) => (
+ onCTAPress(cta, isPNOptInMessage)}
+ />
+);
+
export const MessageDetailsStickyFooter = ({
ctas,
+ firstCTAIsPNOptInMessage,
messageId,
+ secondCTAIsPNOptInMessage,
serviceId
}: MessageDetailsPaymentButtonProps) => {
- const dispatch = useIODispatch();
- const toast = useIOToast();
const safeAreaInsets = useSafeAreaInsets();
const paymentData = useIOSelector(state =>
messagePaymentDataSelector(state, messageId)
);
- const componentVisibility = useIOSelector(state =>
+ const paymentButtonStatus = useIOSelector(state =>
paymentsButtonStateSelector(state, messageId)
);
const canNavigateToPayment = useIOSelector(state =>
canNavigateToPaymentFromMessageSelector(state)
);
- const hidePaymentButton = !paymentData || componentVisibility === "hidden";
- if (!ctas && hidePaymentButton) {
+
+ const linkTo = useLinkTo();
+ const handleOnPress = React.useCallback(
+ (cta: CTA, isPNOptInMessage: boolean) => {
+ if (isPNOptInMessage) {
+ trackPNOptInMessageAccepted();
+ }
+ handleCtaAction(cta, linkTo, serviceId);
+ },
+ [linkTo, serviceId]
+ );
+
+ const footerData = computeFooterData(paymentData, paymentButtonStatus, ctas);
+ if (isNone(footerData)) {
return null;
}
+
+ const isPaymentLoading = paymentButtonStatus === "loading";
return (
- {ctas && (
- <>
-
- {!hidePaymentButton && }
- >
- )}
- {!hidePaymentButton && (
-
- initializeAndNavigateToWalletForPayment(
- messageId,
- getRptIdStringFromPaymentData(paymentData),
- false,
- paymentData.amount,
- canNavigateToPayment,
- dispatch,
- false,
- () => toast.error(I18n.t("genericError"))
- )
- }
- fullWidth
- loading={componentVisibility === "loading"}
- />
+ {foldFooterData(
+ footerData,
+ paymentWithDoubleCTA =>
+ renderPaymentWithDoubleCTA(
+ messageId,
+ paymentWithDoubleCTA.paymentData,
+ canNavigateToPayment,
+ isPaymentLoading,
+ paymentWithDoubleCTA.cta1,
+ firstCTAIsPNOptInMessage,
+ paymentWithDoubleCTA.cta2,
+ secondCTAIsPNOptInMessage,
+ handleOnPress
+ ),
+ paymentWithCTA =>
+ renderPaymentWithCTA(
+ messageId,
+ paymentWithCTA.paymentData,
+ canNavigateToPayment,
+ isPaymentLoading,
+ paymentWithCTA.cta1,
+ firstCTAIsPNOptInMessage,
+ handleOnPress
+ ),
+ doubleCTA =>
+ renderDoubleCTA(
+ doubleCTA.cta1,
+ firstCTAIsPNOptInMessage,
+ doubleCTA.cta2,
+ secondCTAIsPNOptInMessage,
+ handleOnPress
+ ),
+ payment =>
+ renderPayment(
+ messageId,
+ payment.paymentData,
+ canNavigateToPayment,
+ isPaymentLoading
+ ),
+ cta => renderCTA(cta.cta1, firstCTAIsPNOptInMessage, handleOnPress),
+ () => null
)}
);
diff --git a/ts/features/messages/components/MessageDetail/__tests__/MessageDetailsPaymentButton.test.tsx b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailsPaymentButton.test.tsx
new file mode 100644
index 00000000000..7a1c32f65c9
--- /dev/null
+++ b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailsPaymentButton.test.tsx
@@ -0,0 +1,50 @@
+import * as React from "react";
+import { createStore } from "redux";
+import { applicationChangeState } from "../../../../../store/actions/application";
+import { preferencesDesignSystemSetEnabled } from "../../../../../store/actions/persistedPreferences";
+import { appReducer } from "../../../../../store/reducers";
+import { MessageDetailsPaymentButton } from "../MessageDetailsPaymentButton";
+import { renderScreenWithNavigationStoreContext } from "../../../../../utils/testWrapper";
+import { PaymentData, UIMessageId } from "../../../types";
+
+describe("MessageDetailsPaymentButton", () => {
+ it("should match snapshot when not loading", () => {
+ const screen = renderScreen(false);
+ expect(screen.toJSON()).toMatchSnapshot();
+ });
+ it("should match snapshot when loading", () => {
+ const screen = renderScreen(true);
+ expect(screen.toJSON()).toMatchSnapshot();
+ });
+});
+
+const renderScreen = (isLoading: boolean) => {
+ const initialState = appReducer(undefined, applicationChangeState("active"));
+ const designSystemState = appReducer(
+ initialState,
+ preferencesDesignSystemSetEnabled({ isDesignSystemEnabled: true })
+ );
+ const store = createStore(appReducer, designSystemState as any);
+
+ return renderScreenWithNavigationStoreContext(
+ () => (
+
+ ),
+ "DUMMY",
+ {},
+ store
+ );
+};
diff --git a/ts/features/messages/components/MessageDetail/__tests__/MessageDetailsScrollViewAdditionalSpace.test.tsx b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailsScrollViewAdditionalSpace.test.tsx
index c5c66592afc..bc1d8571115 100644
--- a/ts/features/messages/components/MessageDetail/__tests__/MessageDetailsScrollViewAdditionalSpace.test.tsx
+++ b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailsScrollViewAdditionalSpace.test.tsx
@@ -16,34 +16,49 @@ describe("MessageDetailsScrollViewAdditionalSpace", () => {
jest
.spyOn(payments, "isPaymentsButtonVisibleSelector")
.mockReturnValue(false);
- const component = renderComponent(false);
+ const component = renderComponent(false, false);
expect(component.toJSON()).toMatchSnapshot();
});
- it("Should match snapshot with hidden button and CTAs", () => {
+ it("Should match snapshot with hidden button and both CTAs", () => {
jest
.spyOn(payments, "isPaymentsButtonVisibleSelector")
.mockReturnValue(false);
- const component = renderComponent(true);
+ const component = renderComponent(true, true);
+ expect(component.toJSON()).toMatchSnapshot();
+ });
+ it("Should match snapshot with hidden button and a single CTA", () => {
+ jest
+ .spyOn(payments, "isPaymentsButtonVisibleSelector")
+ .mockReturnValue(false);
+ const component = renderComponent(true, false);
expect(component.toJSON()).toMatchSnapshot();
});
it("Should match snapshot with button and no CTAs", () => {
jest
.spyOn(payments, "isPaymentsButtonVisibleSelector")
.mockReturnValue(true);
- const component = renderComponent(false);
+ const component = renderComponent(false, false);
+ expect(component.toJSON()).toMatchSnapshot();
+ });
+ it("Should match snapshot with button and both CTA", () => {
+ jest
+ .spyOn(payments, "isPaymentsButtonVisibleSelector")
+ .mockReturnValue(true);
+ const component = renderComponent(true, true);
expect(component.toJSON()).toMatchSnapshot();
});
- it("Should match snapshot with button and CTAs", () => {
+ it("Should match snapshot with button and a single CTA", () => {
jest
.spyOn(payments, "isPaymentsButtonVisibleSelector")
.mockReturnValue(true);
- const component = renderComponent(true);
+ const component = renderComponent(true, false);
expect(component.toJSON()).toMatchSnapshot();
});
});
const renderComponent = (
- hasCTAS: boolean,
+ hasCTA1: boolean,
+ hasCTA2: boolean,
messageId: UIMessageId = "01HRW5J2QYMH3FWAA5CYGXSC84" as UIMessageId
) => {
const globalState = appReducer(undefined, applicationChangeState("active"));
@@ -52,7 +67,8 @@ const renderComponent = (
() => (
),
"DUMMY",
diff --git a/ts/features/messages/components/MessageDetail/__tests__/MessageDetailsStickyFooter.test.tsx b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailsStickyFooter.test.tsx
index 7e076a8e5c8..0e8103073f8 100644
--- a/ts/features/messages/components/MessageDetail/__tests__/MessageDetailsStickyFooter.test.tsx
+++ b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailsStickyFooter.test.tsx
@@ -128,6 +128,8 @@ const renderComponent = (ctas?: CTAS) => {
messageId={"01HRW6GJBD594Z0K9B4D6KAERC" as UIMessageId}
serviceId={"01HRW6GS171WY97ZBJ5BPFJ625" as ServiceId}
ctas={ctas}
+ firstCTAIsPNOptInMessage={false}
+ secondCTAIsPNOptInMessage={false}
/>
),
"DUMMY",
diff --git a/ts/components/cta/__test__/__snapshots__/CTAsBar.test.tsx.snap b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailsPaymentButton.test.tsx.snap
similarity index 59%
rename from ts/components/cta/__test__/__snapshots__/CTAsBar.test.tsx.snap
rename to ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailsPaymentButton.test.tsx.snap
index e5823eeaabd..b66722bdcd5 100644
--- a/ts/components/cta/__test__/__snapshots__/CTAsBar.test.tsx.snap
+++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailsPaymentButton.test.tsx.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`CTAsBar should match snapshot with both CTAs 1`] = `
+exports[`MessageDetailsPaymentButton should match snapshot when loading 1`] = `
-
-
-
- My CTA 2
-
-
-
-
-
-
+
-
-
- My CTA 1
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -560,7 +578,7 @@ exports[`CTAsBar should match snapshot with both CTAs 1`] = `
`;
-exports[`CTAsBar should match snapshot with one CTA 1`] = `
+exports[`MessageDetailsPaymentButton should match snapshot when not loading 1`] = `
-
-
-
- My CTA 1
-
-
-
+ Pay
+
diff --git a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailsScrollViewAdditionalSpace.test.tsx.snap b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailsScrollViewAdditionalSpace.test.tsx.snap
index 38203e85a2b..c9138d1c9bf 100644
--- a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailsScrollViewAdditionalSpace.test.tsx.snap
+++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailsScrollViewAdditionalSpace.test.tsx.snap
@@ -1,6 +1,684 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`MessageDetailsScrollViewAdditionalSpace Should match snapshot with button and CTAs 1`] = `
+exports[`MessageDetailsScrollViewAdditionalSpace Should match snapshot with button and a single CTA 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DUMMY
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`MessageDetailsScrollViewAdditionalSpace Should match snapshot with button and both CTA 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ DUMMY
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`MessageDetailsScrollViewAdditionalSpace Should match snapshot with button and no CTAs 1`] = `
@@ -339,7 +1017,7 @@ exports[`MessageDetailsScrollViewAdditionalSpace Should match snapshot with butt
`;
-exports[`MessageDetailsScrollViewAdditionalSpace Should match snapshot with button and no CTAs 1`] = `
+exports[`MessageDetailsScrollViewAdditionalSpace Should match snapshot with hidden button and a single CTA 1`] = `
@@ -678,7 +1356,7 @@ exports[`MessageDetailsScrollViewAdditionalSpace Should match snapshot with butt
`;
-exports[`MessageDetailsScrollViewAdditionalSpace Should match snapshot with hidden button and CTAs 1`] = `
+exports[`MessageDetailsScrollViewAdditionalSpace Should match snapshot with hidden button and both CTAs 1`] = `
@@ -1339,7 +2017,7 @@ exports[`MessageDetailsScrollViewAdditionalSpace Should match snapshot with hidd
diff --git a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailsStickyFooter.test.tsx.snap b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailsStickyFooter.test.tsx.snap
index 86f13b0a1a6..93ca6778f3c 100644
--- a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailsStickyFooter.test.tsx.snap
+++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailsStickyFooter.test.tsx.snap
@@ -349,230 +349,213 @@ exports[`MessageDetailsStickyFooter should match snapshot with both CTAs and no
}
>
-
-
- CTA 2
-
-
+ CTA 1
+
+
+
+
-
-
-
-
- CTA 1
-
-
-
+ CTA 2
+
@@ -939,230 +922,110 @@ exports[`MessageDetailsStickyFooter should match snapshot with both CTAs and vis
}
>
-
-
-
- CTA 2
-
-
-
-
-
-
-
-
-
- CTA 1
-
-
-
+ Pay
+
@@ -1174,11 +1037,10 @@ exports[`MessageDetailsStickyFooter should match snapshot with both CTAs and vis
}
/>
+
+ CTA 1
+
+
+
+
+
+
@@ -1245,38 +1202,28 @@ exports[`MessageDetailsStickyFooter should match snapshot with both CTAs and vis
accessibilityElementsHidden={true}
accessible={false}
allowFontScaling={false}
- color="white"
- defaultColor="white"
- defaultWeight="Bold"
ellipsizeMode="tail"
- font="TitilliumWeb"
- fontStyle={
- Object {
- "fontSize": 16,
- }
- }
importantForAccessibility="no-hide-descendants"
maxFontSizeMultiplier={1.3}
numberOfLines={1}
style={
Array [
Object {
- "alignSelf": "center",
- },
- Object {
- "fontSize": 16,
- },
- Object {
- "color": "#FFFFFF",
"fontFamily": "Titillium Web",
+ "fontSize": 16,
"fontStyle": "normal",
"fontWeight": "700",
},
+ Object {
+ "color": "#0073E6",
+ },
+ Object {
+ "color": undefined,
+ },
]
}
- weight="Bold"
>
- Pay
+ CTA 2
@@ -3384,126 +3331,110 @@ exports[`MessageDetailsStickyFooter should match snapshot with one CTA and no pa
}
>
-
-
-
- CTA 1
-
-
-
+ CTA 1
+
@@ -3869,137 +3800,6 @@ exports[`MessageDetailsStickyFooter should match snapshot with one CTA and visib
]
}
>
-
-
-
-
-
-
- CTA 1
-
-
-
-
-
-
-
+
+
+
+
+
+ CTA 1
+
+
+
+
diff --git a/ts/features/messages/screens/MessageDetailsScreen.tsx b/ts/features/messages/screens/MessageDetailsScreen.tsx
index 6005a87a353..6eebac0b4b7 100644
--- a/ts/features/messages/screens/MessageDetailsScreen.tsx
+++ b/ts/features/messages/screens/MessageDetailsScreen.tsx
@@ -41,6 +41,12 @@ import { userSelectedPaymentRptIdSelector } from "../store/reducers/payments";
import { MessageDetailsStickyFooter } from "../components/MessageDetail/MessageDetailsStickyFooter";
import { MessageDetailsScrollViewAdditionalSpace } from "../components/MessageDetail/MessageDetailsScrollViewAdditionalSpace";
import { serviceMetadataByIdSelector } from "../../../store/reducers/entities/services/servicesById";
+import { isPNOptInMessage } from "../../pn/utils";
+import { useOnFirstRender } from "../../../utils/hooks/useOnFirstRender";
+import {
+ trackPNOptInMessageCTADisplaySuccess,
+ trackPNOptInMessageOpened
+} from "../../pn/analytics";
const styles = StyleSheet.create({
scrollContentContainer: {
@@ -112,13 +118,27 @@ export const MessageDetailsScreen = (props: MessageDetailsScreenProps) => {
[messageMarkdown, serviceId, serviceMetadata]
);
+ // Use the store since `isPNOptInMessage` is not a selector but an utility
+ // that uses a backend status configuration that is normally updated every
+ // minute. We do not want to cause a re-rendering or recompute the value
+ const store = useIOStore();
+ const state = store.getState();
+ const pnOptInMessageInfo = isPNOptInMessage(maybeCTAs, serviceId, state);
+
useHeaderSecondLevel({
title: "",
goBack,
supportRequest: true
});
- const store = useIOStore();
+ useOnFirstRender(
+ () => {
+ trackPNOptInMessageOpened();
+ trackPNOptInMessageCTADisplaySuccess();
+ },
+ () => pnOptInMessageInfo.isPNOptInMessage
+ );
+
useFocusEffect(
useCallback(() => {
const globalState = store.getState();
@@ -184,13 +204,16 @@ export const MessageDetailsScreen = (props: MessageDetailsScreenProps) => {
>
diff --git a/ts/features/messages/utils/index.ts b/ts/features/messages/utils/index.ts
index 86d14002ed7..4e29b4fb26e 100644
--- a/ts/features/messages/utils/index.ts
+++ b/ts/features/messages/utils/index.ts
@@ -16,6 +16,7 @@ import { PaymentAmount } from "../../../../definitions/backend/PaymentAmount";
import { getAmountFromPaymentAmount } from "../../../utils/payment";
import { trackPNPaymentStart } from "../../pn/analytics";
import { addUserSelectedPaymentRptId } from "../store/actions";
+import { Action } from "../../../store/actions/types";
import { MessagePaymentExpirationInfo } from "./messages";
export const gapBetweenItemsInAGrid = 8;
@@ -55,7 +56,7 @@ export const initializeAndNavigateToWalletForPayment = (
isPaidOrHasAnError: boolean,
paymentAmount: PaymentAmount | undefined,
canNavigateToPayment: boolean,
- dispatch: Dispatch,
+ dispatch: Dispatch,
isPNPayment: boolean,
decodeErrorCallback: (() => void) | undefined,
preNavigationCallback: (() => void) | undefined = undefined
diff --git a/ts/features/pn/hooks/usePNOptInMessage.ts b/ts/features/pn/hooks/usePNOptInMessage.ts
deleted file mode 100644
index 47c87c297ea..00000000000
--- a/ts/features/pn/hooks/usePNOptInMessage.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-import { isPNOptInMessage } from "../utils";
-import { useOnFirstRender } from "../../../utils/hooks/useOnFirstRender";
-import {
- trackPNOptInMessageCTADisplaySuccess,
- trackPNOptInMessageOpened
-} from "../analytics";
-import { ServiceId } from "../../../../definitions/backend/ServiceId";
-import { useIOStore } from "../../../store/hooks";
-import { CTAS } from "../../messages/types/MessageCTA";
-
-export const usePNOptInMessage = (
- ctas: CTAS | undefined,
- serviceId: ServiceId
-) => {
- // Use the store since `isPNOptInMessage` is not a selector but an utility
- // that uses a backend status configuration that is normally updated every
- // minute. We do not want to cause a re-rendering or recompute the value
- const store = useIOStore();
- const state = store.getState();
- const pnOptInMessageInfo = isPNOptInMessage(ctas, serviceId, state);
-
- useOnFirstRender(
- () => {
- trackPNOptInMessageOpened();
- trackPNOptInMessageCTADisplaySuccess();
- },
- () => pnOptInMessageInfo.isPNOptInMessage
- );
-
- return pnOptInMessageInfo;
-};
diff --git a/ts/features/pn/utils/__tests__/index.test.ts b/ts/features/pn/utils/__tests__/index.test.ts
index 23bccc5f4d6..9877ffdf70e 100644
--- a/ts/features/pn/utils/__tests__/index.test.ts
+++ b/ts/features/pn/utils/__tests__/index.test.ts
@@ -43,8 +43,8 @@ type IsPNOptInMessageTestInputType = {
};
output: {
isPNOptInMessage: boolean;
- cta1HasServiceNavigationLink: boolean;
- cta2HasServiceNavigationLink: boolean;
+ cta1LinksToPNService: boolean;
+ cta2LinksToPNService: boolean;
};
};
@@ -58,8 +58,8 @@ const isPNOptInMessageTestInput: Array = [
},
output: {
isPNOptInMessage: true,
- cta1HasServiceNavigationLink: true,
- cta2HasServiceNavigationLink: true
+ cta1LinksToPNService: true,
+ cta2LinksToPNService: true
}
},
{
@@ -75,8 +75,8 @@ const isPNOptInMessageTestInput: Array = [
},
output: {
isPNOptInMessage: true,
- cta1HasServiceNavigationLink: true,
- cta2HasServiceNavigationLink: false
+ cta1LinksToPNService: true,
+ cta2LinksToPNService: false
}
},
{
@@ -95,8 +95,8 @@ const isPNOptInMessageTestInput: Array = [
},
output: {
isPNOptInMessage: true,
- cta1HasServiceNavigationLink: true,
- cta2HasServiceNavigationLink: false
+ cta1LinksToPNService: true,
+ cta2LinksToPNService: false
}
},
{
@@ -115,8 +115,8 @@ const isPNOptInMessageTestInput: Array = [
},
output: {
isPNOptInMessage: true,
- cta1HasServiceNavigationLink: false,
- cta2HasServiceNavigationLink: true
+ cta1LinksToPNService: false,
+ cta2LinksToPNService: true
}
},
{
@@ -134,8 +134,8 @@ const isPNOptInMessageTestInput: Array = [
},
output: {
isPNOptInMessage: false,
- cta1HasServiceNavigationLink: false,
- cta2HasServiceNavigationLink: false
+ cta1LinksToPNService: false,
+ cta2LinksToPNService: false
}
},
{
@@ -157,8 +157,8 @@ const isPNOptInMessageTestInput: Array = [
},
output: {
isPNOptInMessage: false,
- cta1HasServiceNavigationLink: false,
- cta2HasServiceNavigationLink: false
+ cta1LinksToPNService: false,
+ cta2LinksToPNService: false
}
},
{
@@ -171,8 +171,8 @@ const isPNOptInMessageTestInput: Array = [
},
output: {
isPNOptInMessage: false,
- cta1HasServiceNavigationLink: false,
- cta2HasServiceNavigationLink: false
+ cta1LinksToPNService: false,
+ cta2LinksToPNService: false
}
},
{
@@ -185,8 +185,8 @@ const isPNOptInMessageTestInput: Array = [
},
output: {
isPNOptInMessage: false,
- cta1HasServiceNavigationLink: false,
- cta2HasServiceNavigationLink: false
+ cta1LinksToPNService: false,
+ cta2LinksToPNService: false
}
},
{
@@ -199,8 +199,8 @@ const isPNOptInMessageTestInput: Array = [
},
output: {
isPNOptInMessage: false,
- cta1HasServiceNavigationLink: false,
- cta2HasServiceNavigationLink: false
+ cta1LinksToPNService: false,
+ cta2LinksToPNService: false
}
},
{
@@ -224,8 +224,8 @@ const isPNOptInMessageTestInput: Array = [
},
output: {
isPNOptInMessage: false,
- cta1HasServiceNavigationLink: false,
- cta2HasServiceNavigationLink: false
+ cta1LinksToPNService: false,
+ cta2LinksToPNService: false
}
},
{
@@ -243,8 +243,8 @@ const isPNOptInMessageTestInput: Array = [
},
output: {
isPNOptInMessage: false,
- cta1HasServiceNavigationLink: false,
- cta2HasServiceNavigationLink: false
+ cta1LinksToPNService: false,
+ cta2LinksToPNService: false
}
}
];
diff --git a/ts/features/pn/utils/index.ts b/ts/features/pn/utils/index.ts
index d70616217f3..48fea153997 100644
--- a/ts/features/pn/utils/index.ts
+++ b/ts/features/pn/utils/index.ts
@@ -24,8 +24,8 @@ export function getNotificationStatusInfo(status: NotificationStatus) {
export type PNOptInMessageInfo = {
isPNOptInMessage: boolean;
- cta1HasServiceNavigationLink: boolean;
- cta2HasServiceNavigationLink: boolean;
+ cta1LinksToPNService: boolean;
+ cta2LinksToPNService: boolean;
};
export const isPNOptInMessage = (
@@ -50,24 +50,24 @@ export const isPNOptInMessage = (
ctas,
O.fromNullable,
O.map(ctas => ({
- cta1HasServiceNavigationLink: isServiceDetailNavigationLink(
+ cta1LinksToPNService: isServiceDetailNavigationLink(
ctas.cta_1.action
),
- cta2HasServiceNavigationLink:
+ cta2LinksToPNService:
!!ctas.cta_2 && isServiceDetailNavigationLink(ctas.cta_2.action)
})),
O.map(ctaNavigationLinkInfo => ({
isPNOptInMessage:
- ctaNavigationLinkInfo.cta1HasServiceNavigationLink ||
- ctaNavigationLinkInfo.cta2HasServiceNavigationLink,
+ ctaNavigationLinkInfo.cta1LinksToPNService ||
+ ctaNavigationLinkInfo.cta2LinksToPNService,
...ctaNavigationLinkInfo
}))
)
),
O.getOrElse(() => ({
isPNOptInMessage: false,
- cta1HasServiceNavigationLink: false,
- cta2HasServiceNavigationLink: false
+ cta1LinksToPNService: false,
+ cta2LinksToPNService: false
}))
);