diff --git a/ts/features/messages/components/MessageDetail/MessageDetailHeader.tsx b/ts/features/messages/components/MessageDetail/MessageDetailHeader.tsx
new file mode 100644
index 00000000000..849cf89df7c
--- /dev/null
+++ b/ts/features/messages/components/MessageDetail/MessageDetailHeader.tsx
@@ -0,0 +1,48 @@
+import { Divider, H3, LabelSmall, VSpacer } from "@pagopa/io-app-design-system";
+import React, { PropsWithChildren } from "react";
+import { StyleSheet, View } from "react-native";
+import { localeDateFormat } from "../../../../utils/locale";
+import I18n from "../../../../i18n";
+import { ServicePublic } from "../../../../../definitions/backend/ServicePublic";
+
+export type MessageDetailHeaderProps = PropsWithChildren<{
+ createdAt: Date;
+ subject: string;
+ sender?: string;
+ service?: ServicePublic;
+}>;
+
+const styles = StyleSheet.create({
+ header: {
+ paddingHorizontal: 24
+ }
+});
+
+const MessageHeaderContent = ({
+ subject,
+ createdAt
+}: MessageDetailHeaderProps) => (
+ <>
+
{subject}
+
+
+ {localeDateFormat(
+ createdAt,
+ I18n.t("global.dateFormats.fullFormatShortMonthLiteralWithTime")
+ )}
+
+ >
+);
+
+export const MessageDetailHeader = ({
+ children,
+ ...rest
+}: MessageDetailHeaderProps) => (
+
+ {children}
+
+
+
+ {/* TODO: Add MessageHeaderService */}
+
+);
diff --git a/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx
new file mode 100644
index 00000000000..5539b0d3fdc
--- /dev/null
+++ b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx
@@ -0,0 +1,35 @@
+import React, { ComponentProps } from "react";
+import { render } from "@testing-library/react-native";
+import { MessageDetailHeader } from "../MessageDetailHeader";
+import { ServicePublic } from "../../../../../../definitions/backend/ServicePublic";
+
+const service = {
+ service_id: "serviceId",
+ service_name: "health",
+ organization_name: "Organization foo",
+ department_name: "Department one",
+ organization_fiscal_code: "OFSAAAAAA"
+} as ServicePublic;
+
+const defaultProps: ComponentProps = {
+ subject: "Subject",
+ createdAt: new Date("2021-10-18T16:00:30.541Z")
+};
+
+describe("MessageDetailHeader component", () => {
+ it("should match the snapshot with default props", () => {
+ const component = render();
+ expect(component.toJSON()).toMatchSnapshot();
+ });
+
+ it("should match the snapshot with all props", () => {
+ const component = render(
+
+ );
+ expect(component.toJSON()).toMatchSnapshot();
+ });
+});
diff --git a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap
new file mode 100644
index 00000000000..d1597d22480
--- /dev/null
+++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap
@@ -0,0 +1,191 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`MessageDetailHeader component should match the snapshot with all props 1`] = `
+
+
+ Subject
+
+
+
+ 18 Oct 2021, 16:00
+
+
+
+
+`;
+
+exports[`MessageDetailHeader component should match the snapshot with default props 1`] = `
+
+
+ Subject
+
+
+
+ 18 Oct 2021, 16:00
+
+
+
+
+`;
diff --git a/ts/features/pn/__mocks__/message.ts b/ts/features/pn/__mocks__/message.ts
new file mode 100644
index 00000000000..6bfc8eeaccb
--- /dev/null
+++ b/ts/features/pn/__mocks__/message.ts
@@ -0,0 +1,46 @@
+import { ThirdPartyMessageWithContent } from "../../../../definitions/backend/ThirdPartyMessageWithContent";
+import { message_1 } from "../../messages/__mocks__/message";
+import { ATTACHMENT_CATEGORY } from "../../messages/types/attachmentCategory";
+
+export const thirdPartyMessage: ThirdPartyMessageWithContent = {
+ ...message_1,
+ created_at: new Date("2020-01-01T00:00:00.000Z"),
+ third_party_message: {
+ details: {
+ abstract: "######## abstract ########",
+ attachments: [
+ {
+ messageId: message_1.id,
+ id: "1",
+ displayName: "A First Attachment",
+ contentType: "application/pdf",
+ category: ATTACHMENT_CATEGORY.DOCUMENT,
+ resourceUrl: { href: "/resource/attachment1.pdf" }
+ },
+ {
+ messageId: message_1.id,
+ id: "2",
+ displayName: "A Second Attachment",
+ contentType: "application/pdf",
+ category: ATTACHMENT_CATEGORY.DOCUMENT,
+ resourceUrl: { href: "/resource/attachment2.pdf" }
+ }
+ ],
+ iun: "731143-7-0317-8200-0",
+ subject: "######## subject ########",
+ recipients: [
+ {
+ recipientType: "-",
+ taxId: "AAABBB00A00A000A",
+ denomination: "AaAaAa BbBbBb",
+ payment: {
+ noticeCode: "026773337463073118",
+ creditorTaxId: "00000000009"
+ }
+ }
+ ],
+ notificationStatusHistory: [],
+ senderDenomination: "Sender denomination"
+ }
+ }
+};
diff --git a/ts/features/pn/components/LegacyMessageDetails.tsx b/ts/features/pn/components/LegacyMessageDetails.tsx
new file mode 100644
index 00000000000..78d59087e5e
--- /dev/null
+++ b/ts/features/pn/components/LegacyMessageDetails.tsx
@@ -0,0 +1,193 @@
+import React, { useCallback, createRef, useRef } from "react";
+import { pipe } from "fp-ts/lib/function";
+import * as O from "fp-ts/lib/Option";
+import * as RA from "fp-ts/lib/ReadonlyArray";
+import * as SEP from "fp-ts/lib/Separated";
+import { View } from "react-native";
+import { ScrollView } from "react-native-gesture-handler";
+import {
+ IOVisualCostants,
+ ListItemInfoCopy,
+ VSpacer
+} from "@pagopa/io-app-design-system";
+import { ServicePublic } from "../../../../definitions/backend/ServicePublic";
+import { H5 } from "../../../components/core/typography/H5";
+import I18n from "../../../i18n";
+import { useIOSelector } from "../../../store/hooks";
+import { pnFrontendUrlSelector } from "../../../store/reducers/backendStatus";
+import { UIAttachment, UIMessageId } from "../../messages/types";
+import { clipboardSetStringWithFeedback } from "../../../utils/clipboard";
+import { LegacyMessageAttachments } from "../../messages/components/LegacyMessageAttachments";
+import NavigationService from "../../../navigation/NavigationService";
+import PN_ROUTES from "../navigation/routes";
+import { PNMessage } from "../store/types/types";
+import { NotificationPaymentInfo } from "../../../../definitions/pn/NotificationPaymentInfo";
+import { trackPNAttachmentOpening } from "../analytics";
+import { DSFullWidthComponent } from "../../design-system/components/DSFullWidthComponent";
+import StatusContent from "../../../components/SectionStatus/StatusContent";
+import {
+ getStatusTextColor,
+ statusColorMap,
+ statusIconMap
+} from "../../../components/SectionStatus";
+import { LevelEnum } from "../../../../definitions/content/SectionStatus";
+import { ATTACHMENT_CATEGORY } from "../../messages/types/attachmentCategory";
+import { maxVisiblePaymentCountGenerator } from "../utils";
+import { LegacyMessageDetailsContent } from "./LegacyMessageDetailsContent";
+import { MessageDetailsHeader } from "./MessageDetailsHeader";
+import { MessageDetailsSection } from "./MessageDetailsSection";
+import { MessageTimeline } from "./MessageTimeline";
+import { MessageTimelineCTA } from "./MessageTimelineCTA";
+import { MessageF24 } from "./MessageF24";
+import { MessagePayments } from "./MessagePayments";
+import { MessageFooter } from "./MessageFooter";
+import { MessagePaymentBottomSheet } from "./MessagePaymentBottomSheet";
+
+type Props = Readonly<{
+ messageId: UIMessageId;
+ message: PNMessage;
+ service: ServicePublic | undefined;
+ payments: ReadonlyArray | undefined;
+}>;
+
+export const LegacyMessageDetails = ({
+ message,
+ messageId,
+ service,
+ payments
+}: Props) => {
+ const viewRef = createRef();
+ const presentPaymentsBottomSheetRef = useRef<() => void>();
+ const frontendUrl = useIOSelector(pnFrontendUrlSelector);
+
+ const partitionedAttachments = pipe(
+ message.attachments,
+ O.fromNullable,
+ O.getOrElse>(() => []),
+ RA.partition(attachment => attachment.category === ATTACHMENT_CATEGORY.F24)
+ );
+
+ const f24List = SEP.right(partitionedAttachments);
+ const attachmentList = SEP.left(partitionedAttachments);
+
+ const isCancelled = message.isCancelled ?? false;
+ const completedPaymentNoticeCodes = isCancelled
+ ? message.completedPayments
+ : undefined;
+
+ const openAttachment = useCallback(
+ (attachment: UIAttachment) => {
+ trackPNAttachmentOpening(attachment.category);
+ NavigationService.navigate(PN_ROUTES.MESSAGE_ATTACHMENT, {
+ messageId,
+ attachmentId: attachment.id,
+ category: attachment.category
+ });
+ },
+ [messageId]
+ );
+
+ const maxVisiblePaymentCount = maxVisiblePaymentCountGenerator();
+ const scrollViewRef = React.createRef();
+
+ return (
+ <>
+
+ {service && }
+
+
+ {isCancelled && (
+ <>
+
+
+
+ {I18n.t("features.pn.details.cancelledMessage.body")}
+
+
+ >
+ )}
+
+ {RA.isNonEmpty(attachmentList) && (
+
+
+
+ )}
+
+
+ {!isCancelled && RA.isNonEmpty(f24List) ? (
+ <>
+
+
+ >
+ ) : null}
+
+
+ clipboardSetStringWithFeedback(message.iun)}
+ accessibilityLabel={I18n.t("features.pn.details.infoSection.iun")}
+ label={I18n.t("features.pn.details.infoSection.iun")}
+ />
+
+ {I18n.t("features.pn.details.timeline.title")}
+
+
+ {
+ scrollViewRef.current?.scrollToEnd({ animated: true });
+ }}
+ />
+ {frontendUrl.length > 0 && }
+
+
+
+ {payments && !isCancelled && (
+
+ )}
+
+
+ >
+ );
+};
diff --git a/ts/features/pn/components/LegacyMessageDetailsContent.tsx b/ts/features/pn/components/LegacyMessageDetailsContent.tsx
new file mode 100644
index 00000000000..59cb0f34fbe
--- /dev/null
+++ b/ts/features/pn/components/LegacyMessageDetailsContent.tsx
@@ -0,0 +1,31 @@
+import React from "react";
+import { View, ViewProps, StyleSheet } from "react-native";
+import { Body } from "../../../components/core/typography/Body";
+import { H1 } from "../../../components/core/typography/H1";
+import { H2 } from "../../../components/core/typography/H2";
+import { PNMessage } from "../store/types/types";
+import customVariables from "../../../theme/variables";
+import { isStringNullyOrEmpty } from "../../../utils/strings";
+
+const styles = StyleSheet.create({
+ subject: {
+ marginTop: customVariables.spacerExtrasmallHeight
+ },
+ abstract: {
+ marginTop: customVariables.spacerExtrasmallHeight
+ }
+});
+
+type Props = Readonly<{ message: PNMessage }> & ViewProps;
+
+export const LegacyMessageDetailsContent = (props: Props) => (
+
+ {!isStringNullyOrEmpty(props.message.senderDenomination) && (
+ {props.message.senderDenomination}
+ )}
+ {props.message.subject}
+ {!isStringNullyOrEmpty(props.message.abstract) && (
+ {props.message.abstract}
+ )}
+
+);
diff --git a/ts/features/pn/components/MessageDetails.tsx b/ts/features/pn/components/MessageDetails.tsx
index a2370ecabd8..d6f25513ce4 100644
--- a/ts/features/pn/components/MessageDetails.tsx
+++ b/ts/features/pn/components/MessageDetails.tsx
@@ -1,193 +1,27 @@
-import React, { useCallback, createRef, useRef } from "react";
-import { pipe } from "fp-ts/lib/function";
-import * as O from "fp-ts/lib/Option";
-import * as RA from "fp-ts/lib/ReadonlyArray";
-import * as SEP from "fp-ts/lib/Separated";
-import { View } from "react-native";
-import { ScrollView } from "react-native-gesture-handler";
-import {
- IOVisualCostants,
- ListItemInfoCopy,
- VSpacer
-} from "@pagopa/io-app-design-system";
+import React from "react";
+import { ScrollView } from "react-native";
import { ServicePublic } from "../../../../definitions/backend/ServicePublic";
-import { H5 } from "../../../components/core/typography/H5";
-import I18n from "../../../i18n";
-import { useIOSelector } from "../../../store/hooks";
-import { pnFrontendUrlSelector } from "../../../store/reducers/backendStatus";
-import { UIAttachment, UIMessageId } from "../../messages/types";
-import { clipboardSetStringWithFeedback } from "../../../utils/clipboard";
-import { LegacyMessageAttachments } from "../../messages/components/LegacyMessageAttachments";
-import NavigationService from "../../../navigation/NavigationService";
-import PN_ROUTES from "../navigation/routes";
+import { UIMessageId } from "../../messages/types";
import { PNMessage } from "../store/types/types";
import { NotificationPaymentInfo } from "../../../../definitions/pn/NotificationPaymentInfo";
-import { trackPNAttachmentOpening } from "../analytics";
-import { DSFullWidthComponent } from "../../design-system/components/DSFullWidthComponent";
-import StatusContent from "../../../components/SectionStatus/StatusContent";
-import {
- getStatusTextColor,
- statusColorMap,
- statusIconMap
-} from "../../../components/SectionStatus";
-import { LevelEnum } from "../../../../definitions/content/SectionStatus";
-import { ATTACHMENT_CATEGORY } from "../../messages/types/attachmentCategory";
-import { maxVisiblePaymentCountGenerator } from "../utils";
+import { MessageDetailHeader } from "../../messages/components/MessageDetail/MessageDetailHeader";
import { MessageDetailsContent } from "./MessageDetailsContent";
-import { MessageDetailsHeader } from "./MessageDetailsHeader";
-import { MessageDetailsSection } from "./MessageDetailsSection";
-import { MessageTimeline } from "./MessageTimeline";
-import { MessageTimelineCTA } from "./MessageTimelineCTA";
-import { MessageF24 } from "./MessageF24";
-import { MessagePayments } from "./MessagePayments";
-import { MessageFooter } from "./MessageFooter";
-import { MessagePaymentBottomSheet } from "./MessagePaymentBottomSheet";
-type Props = Readonly<{
- messageId: UIMessageId;
+type MessageDetailsProps = {
message: PNMessage;
- service: ServicePublic | undefined;
- payments: ReadonlyArray | undefined;
-}>;
-
-export const MessageDetails = ({
- message,
- messageId,
- service,
- payments
-}: Props) => {
- const viewRef = createRef();
- const presentPaymentsBottomSheetRef = useRef<() => void>();
- const frontendUrl = useIOSelector(pnFrontendUrlSelector);
-
- const partitionedAttachments = pipe(
- message.attachments,
- O.fromNullable,
- O.getOrElse>(() => []),
- RA.partition(attachment => attachment.category === ATTACHMENT_CATEGORY.F24)
- );
-
- const f24List = SEP.right(partitionedAttachments);
- const attachmentList = SEP.left(partitionedAttachments);
-
- const isCancelled = message.isCancelled ?? false;
- const completedPaymentNoticeCodes = isCancelled
- ? message.completedPayments
- : undefined;
-
- const openAttachment = useCallback(
- (attachment: UIAttachment) => {
- trackPNAttachmentOpening(attachment.category);
- NavigationService.navigate(PN_ROUTES.MESSAGE_ATTACHMENT, {
- messageId,
- attachmentId: attachment.id,
- category: attachment.category
- });
- },
- [messageId]
- );
-
- const maxVisiblePaymentCount = maxVisiblePaymentCountGenerator();
- const scrollViewRef = React.createRef();
-
- return (
- <>
-
- {service && }
-
-
- {isCancelled && (
- <>
-
-
-
- {I18n.t("features.pn.details.cancelledMessage.body")}
-
-
- >
- )}
-
- {RA.isNonEmpty(attachmentList) && (
-
-
-
- )}
-
-
- {!isCancelled && RA.isNonEmpty(f24List) ? (
- <>
-
-
- >
- ) : null}
-
-
- clipboardSetStringWithFeedback(message.iun)}
- accessibilityLabel={I18n.t("features.pn.details.infoSection.iun")}
- label={I18n.t("features.pn.details.infoSection.iun")}
- />
-
- {I18n.t("features.pn.details.timeline.title")}
-
-
- {
- scrollViewRef.current?.scrollToEnd({ animated: true });
- }}
- />
- {frontendUrl.length > 0 && }
-
-
-
- {payments && !isCancelled && (
-
- )}
-
-
- >
- );
+ messageId: UIMessageId;
+ service?: ServicePublic;
+ payments?: ReadonlyArray;
};
+
+export const MessageDetails = ({ message, service }: MessageDetailsProps) => (
+
+
+
+
+);
diff --git a/ts/features/pn/components/MessageDetailsContent.tsx b/ts/features/pn/components/MessageDetailsContent.tsx
index 21b33cfef0c..96cbdbbe3ad 100644
--- a/ts/features/pn/components/MessageDetailsContent.tsx
+++ b/ts/features/pn/components/MessageDetailsContent.tsx
@@ -1,31 +1,21 @@
import React from "react";
-import { View, ViewProps, StyleSheet } from "react-native";
-import { Body } from "../../../components/core/typography/Body";
-import { H1 } from "../../../components/core/typography/H1";
-import { H2 } from "../../../components/core/typography/H2";
-import { PNMessage } from "../store/types/types";
-import customVariables from "../../../theme/variables";
-import { isStringNullyOrEmpty } from "../../../utils/strings";
+import { Body, ContentWrapper, VSpacer } from "@pagopa/io-app-design-system";
-const styles = StyleSheet.create({
- subject: {
- marginTop: customVariables.spacerExtrasmallHeight
- },
- abstract: {
- marginTop: customVariables.spacerExtrasmallHeight
- }
-});
+type MessageDetailsContentProps = {
+ abstract?: string;
+};
-type Props = Readonly<{ message: PNMessage }> & ViewProps;
+export const MessageDetailsContent = ({
+ abstract
+}: MessageDetailsContentProps) => {
+ if (abstract === undefined) {
+ return null;
+ }
-export const MessageDetailsContent = (props: Props) => (
-
- {!isStringNullyOrEmpty(props.message.senderDenomination) && (
- {props.message.senderDenomination}
- )}
- {props.message.subject}
- {!isStringNullyOrEmpty(props.message.abstract) && (
- {props.message.abstract}
- )}
-
-);
+ return (
+
+
+ {abstract}
+
+ );
+};
diff --git a/ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx b/ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx
new file mode 100644
index 00000000000..a7ec40d4d30
--- /dev/null
+++ b/ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx
@@ -0,0 +1,188 @@
+import React from "react";
+import * as pot from "@pagopa/ts-commons/lib/pot";
+import { act, fireEvent } from "@testing-library/react-native";
+import { createStore } from "redux";
+import { applicationChangeState } from "../../../../store/actions/application";
+import { appReducer } from "../../../../store/reducers";
+import { LegacyMessageDetails } from "../LegacyMessageDetails";
+import { GlobalState } from "../../../../store/reducers/types";
+import { renderScreenWithNavigationStoreContext } from "../../../../utils/testWrapper";
+import PN_ROUTES from "../../navigation/routes";
+import { UIAttachment, UIMessageId } from "../../../messages/types";
+import { PNMessage } from "../../store/types/types";
+import { Download } from "../../../messages/store/reducers/downloads";
+import { NotificationRecipient } from "../../../../../definitions/pn/NotificationRecipient";
+import { ATTACHMENT_CATEGORY } from "../../../messages/types/attachmentCategory";
+
+const mockedOnAttachmentSelect = jest.fn();
+
+jest.mock("../../../messages/hooks/useAttachmentDownload", () => ({
+ useAttachmentDownload: (
+ _attachment: UIAttachment,
+ _openPreview: (attachment: UIAttachment) => void
+ ) => ({
+ onAttachmentSelect: mockedOnAttachmentSelect,
+ downloadPot: { kind: "PotNone" } as pot.Pot
+ })
+}));
+
+describe("LegacyMessageDetails component", () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it("should not show the cancelled banner when the PN message is not cancelled", () => {
+ const { component } = renderComponent(
+ generateComponentProperties(generatePnMessage())
+ );
+ expect(component.queryByTestId("PnCancelledMessageBanner")).toBeFalsy();
+ });
+
+ it("every attachment item should have a full opaque style when the PN message is not cancelled", () => {
+ const { component } = renderComponent(
+ generateComponentProperties(generatePnMessage())
+ );
+ const messageAttachmentComponents = component.queryAllByTestId(
+ "MessageAttachmentTouchable"
+ );
+ expect(messageAttachmentComponents.length).toBe(2);
+
+ messageAttachmentComponents.forEach(messageAttachmentComponent => {
+ const opacity = messageAttachmentComponent?.props.style.opacity;
+ expect(opacity).toBe(1.0);
+ });
+ });
+
+ it("should show the cancelled banner when the PN message is cancelled", () => {
+ const { component } = renderComponent(
+ generateComponentProperties({
+ ...generatePnMessage(),
+ isCancelled: true
+ })
+ );
+ expect(component.queryByTestId("PnCancelledMessageBanner")).toBeDefined();
+ });
+
+ it("every attachment item should have a semi-transparent style when the PN message is cancelled", () => {
+ const { component } = renderComponent(
+ generateComponentProperties({
+ ...generatePnMessage(),
+ isCancelled: true
+ })
+ );
+ const messageAttachmentComponents = component.queryAllByTestId(
+ "MessageAttachmentTouchable"
+ );
+ expect(messageAttachmentComponents.length).toBe(2);
+
+ messageAttachmentComponents.forEach(messageAttachmentComponent => {
+ const opacity = messageAttachmentComponent?.props.style.opacity;
+ expect(opacity).toBe(0.5);
+ });
+ });
+
+ it("every attachment item should handle input when the PN message not is cancelled", async () => {
+ const { component } = renderComponent(
+ generateComponentProperties(generatePnMessage())
+ );
+ const messageAttachmentComponents = component.queryAllByTestId(
+ "MessageAttachmentTouchable"
+ );
+ expect(messageAttachmentComponents.length).toBe(2);
+ await act(() => {
+ messageAttachmentComponents.forEach(messageAttachmentComponent =>
+ fireEvent.press(messageAttachmentComponent)
+ );
+ });
+ expect(mockedOnAttachmentSelect).toHaveBeenCalledTimes(
+ messageAttachmentComponents.length
+ );
+ });
+
+ it("every attachment item should not handle input when the PN message is cancelled", async () => {
+ const { component } = renderComponent(
+ generateComponentProperties({ ...generatePnMessage(), isCancelled: true })
+ );
+ const messageAttachmentComponents = component.queryAllByTestId(
+ "MessageAttachmentTouchable"
+ );
+ expect(messageAttachmentComponents.length).toBe(2);
+ // eslint-disable-next-line sonarjs/no-identical-functions
+ await act(() => {
+ messageAttachmentComponents.forEach(messageAttachmentComponent =>
+ fireEvent.press(messageAttachmentComponent)
+ );
+ });
+ expect(mockedOnAttachmentSelect).toHaveBeenCalledTimes(0);
+ });
+
+ it("should NOT render the F24 section if there are no multiple F24", () => {
+ const { component } = renderComponent(
+ generateComponentProperties(generatePnMessage())
+ );
+ expect(component.queryByTestId("pn-message-f24-section")).toBeFalsy();
+ });
+});
+
+const generateTestMessageId = () => "00000000000000000000000004" as UIMessageId;
+const generateTestFiscalCode = () => "AAABBB00A00A000A";
+const generatePnMessage = (): PNMessage => ({
+ created_at: new Date(),
+ iun: "731143-7-0317-8200-0",
+ subject: "This is the message subject",
+ senderDenomination: "Sender denomination",
+ abstract: "Message abstract",
+ notificationStatusHistory: [],
+ recipients: [
+ {
+ recipientType: "-",
+ taxId: generateTestFiscalCode(),
+ denomination: "AaAaAa BbBbBb",
+ payment: {
+ noticeCode: "026773337463073118",
+ creditorTaxId: "00000000009"
+ }
+ }
+ ] as Array,
+ attachments: [
+ {
+ messageId: generateTestMessageId(),
+ id: "1",
+ displayName: "A First Attachment",
+ contentType: "application/pdf",
+ category: ATTACHMENT_CATEGORY.DOCUMENT,
+ resourceUrl: { href: "/resource/attachment1.pdf" }
+ },
+ {
+ messageId: generateTestMessageId(),
+ id: "2",
+ displayName: "A Second Attachment",
+ contentType: "application/pdf",
+ category: ATTACHMENT_CATEGORY.DOCUMENT,
+ resourceUrl: { href: "/resource/attachment2.pdf" }
+ }
+ ] as Array
+});
+const generateComponentProperties = (pnMessage: PNMessage) => ({
+ payments: undefined,
+ messageId: generateTestMessageId(),
+ message: pnMessage,
+ service: undefined
+});
+
+const renderComponent = (
+ props: React.ComponentProps
+) => {
+ const globalState = appReducer(undefined, applicationChangeState("active"));
+ const store = createStore(appReducer, globalState as any);
+
+ return {
+ component: renderScreenWithNavigationStoreContext(
+ () => ,
+ PN_ROUTES.MESSAGE_DETAILS,
+ {},
+ store
+ ),
+ store
+ };
+};
diff --git a/ts/features/pn/components/__test__/MessageDetails.test.tsx b/ts/features/pn/components/__test__/MessageDetails.test.tsx
index 11a7c0e7b65..b6c8687b84c 100644
--- a/ts/features/pn/components/__test__/MessageDetails.test.tsx
+++ b/ts/features/pn/components/__test__/MessageDetails.test.tsx
@@ -1,171 +1,38 @@
import React from "react";
-import * as pot from "@pagopa/ts-commons/lib/pot";
-import { act, fireEvent } from "@testing-library/react-native";
-import { createStore } from "redux";
+import configureMockStore from "redux-mock-store";
+import { pipe } from "fp-ts/lib/function";
+import * as O from "fp-ts/lib/Option";
import { applicationChangeState } from "../../../../store/actions/application";
import { appReducer } from "../../../../store/reducers";
import { MessageDetails } from "../MessageDetails";
import { GlobalState } from "../../../../store/reducers/types";
import { renderScreenWithNavigationStoreContext } from "../../../../utils/testWrapper";
-import PN_ROUTES from "../../navigation/routes";
-import { UIAttachment, UIMessageId } from "../../../messages/types";
import { PNMessage } from "../../store/types/types";
-import { Download } from "../../../messages/store/reducers/downloads";
-import { NotificationRecipient } from "../../../../../definitions/pn/NotificationRecipient";
-import { ATTACHMENT_CATEGORY } from "../../../messages/types/attachmentCategory";
+import { thirdPartyMessage } from "../../__mocks__/message";
+import { toPNMessage } from "../../store/types/transformers";
+import { UIMessageId } from "../../../messages/types";
-const mockedOnAttachmentSelect = jest.fn();
-
-jest.mock("../../../messages/hooks/useAttachmentDownload", () => ({
- useAttachmentDownload: (
- _attachment: UIAttachment,
- _openPreview: (attachment: UIAttachment) => void
- ) => ({
- onAttachmentSelect: mockedOnAttachmentSelect,
- downloadPot: { kind: "PotNone" } as pot.Pot
- })
-}));
+const pnMessage = pipe(thirdPartyMessage, toPNMessage, O.toUndefined);
describe("MessageDetails component", () => {
- afterEach(() => {
- jest.clearAllMocks();
- });
-
- it("should not show the cancelled banner when the PN message is not cancelled", () => {
- const { component } = renderComponent(
- generateComponentProperties(generatePnMessage())
- );
- expect(component.queryByTestId("PnCancelledMessageBanner")).toBeFalsy();
- });
-
- it("every attachment item should have a full opaque style when the PN message is not cancelled", () => {
- const { component } = renderComponent(
- generateComponentProperties(generatePnMessage())
- );
- const messageAttachmentComponents = component.queryAllByTestId(
- "MessageAttachmentTouchable"
- );
- expect(messageAttachmentComponents.length).toBe(2);
-
- messageAttachmentComponents.forEach(messageAttachmentComponent => {
- const opacity = messageAttachmentComponent?.props.style.opacity;
- expect(opacity).toBe(1.0);
- });
- });
-
- it("should show the cancelled banner when the PN message is cancelled", () => {
+ it("should match the snapshot", () => {
const { component } = renderComponent(
- generateComponentProperties({
- ...generatePnMessage(),
- isCancelled: true
- })
+ generateComponentProperties(
+ thirdPartyMessage.id as UIMessageId,
+ pnMessage!
+ )
);
- expect(component.queryByTestId("PnCancelledMessageBanner")).toBeDefined();
- });
-
- it("every attachment item should have a semi-transparent style when the PN message is cancelled", () => {
- const { component } = renderComponent(
- generateComponentProperties({
- ...generatePnMessage(),
- isCancelled: true
- })
- );
- const messageAttachmentComponents = component.queryAllByTestId(
- "MessageAttachmentTouchable"
- );
- expect(messageAttachmentComponents.length).toBe(2);
-
- messageAttachmentComponents.forEach(messageAttachmentComponent => {
- const opacity = messageAttachmentComponent?.props.style.opacity;
- expect(opacity).toBe(0.5);
- });
- });
-
- it("every attachment item should handle input when the PN message not is cancelled", async () => {
- const { component } = renderComponent(
- generateComponentProperties(generatePnMessage())
- );
- const messageAttachmentComponents = component.queryAllByTestId(
- "MessageAttachmentTouchable"
- );
- expect(messageAttachmentComponents.length).toBe(2);
- await act(() => {
- messageAttachmentComponents.forEach(messageAttachmentComponent =>
- fireEvent.press(messageAttachmentComponent)
- );
- });
- expect(mockedOnAttachmentSelect).toHaveBeenCalledTimes(
- messageAttachmentComponents.length
- );
- });
-
- it("every attachment item should not handle input when the PN message is cancelled", async () => {
- const { component } = renderComponent(
- generateComponentProperties({ ...generatePnMessage(), isCancelled: true })
- );
- const messageAttachmentComponents = component.queryAllByTestId(
- "MessageAttachmentTouchable"
- );
- expect(messageAttachmentComponents.length).toBe(2);
- // eslint-disable-next-line sonarjs/no-identical-functions
- await act(() => {
- messageAttachmentComponents.forEach(messageAttachmentComponent =>
- fireEvent.press(messageAttachmentComponent)
- );
- });
- expect(mockedOnAttachmentSelect).toHaveBeenCalledTimes(0);
- });
-
- it("should NOT render the F24 section if there are no multiple F24", () => {
- const { component } = renderComponent(
- generateComponentProperties(generatePnMessage())
- );
- expect(component.queryByTestId("pn-message-f24-section")).toBeFalsy();
+ expect(component).toMatchSnapshot();
});
});
-const generateTestMessageId = () => "00000000000000000000000004" as UIMessageId;
-const generateTestFiscalCode = () => "AAABBB00A00A000A";
-const generatePnMessage = (): PNMessage => ({
- iun: "731143-7-0317-8200-0",
- subject: "This is the message subject",
- senderDenomination: "Sender denomination",
- abstract: "Message abstract",
- notificationStatusHistory: [],
- recipients: [
- {
- recipientType: "-",
- taxId: generateTestFiscalCode(),
- denomination: "AaAaAa BbBbBb",
- payment: {
- noticeCode: "026773337463073118",
- creditorTaxId: "00000000009"
- }
- }
- ] as Array,
- attachments: [
- {
- messageId: generateTestMessageId(),
- id: "1",
- displayName: "A First Attachment",
- contentType: "application/pdf",
- category: ATTACHMENT_CATEGORY.DOCUMENT,
- resourceUrl: { href: "/resource/attachment1.pdf" }
- },
- {
- messageId: generateTestMessageId(),
- id: "2",
- displayName: "A Second Attachment",
- contentType: "application/pdf",
- category: ATTACHMENT_CATEGORY.DOCUMENT,
- resourceUrl: { href: "/resource/attachment2.pdf" }
- }
- ] as Array
-});
-const generateComponentProperties = (pnMessage: PNMessage) => ({
+const generateComponentProperties = (
+ messageId: UIMessageId,
+ message: PNMessage
+) => ({
+ messageId,
+ message,
payments: undefined,
- messageId: generateTestMessageId(),
- message: pnMessage,
service: undefined
});
@@ -173,12 +40,13 @@ const renderComponent = (
props: React.ComponentProps
) => {
const globalState = appReducer(undefined, applicationChangeState("active"));
- const store = createStore(appReducer, globalState as any);
+ const mockStore = configureMockStore();
+ const store: ReturnType = mockStore(globalState);
return {
component: renderScreenWithNavigationStoreContext(
() => ,
- PN_ROUTES.MESSAGE_DETAILS,
+ "DUMMY_ROUTE",
{},
store
),
diff --git a/ts/features/pn/components/__test__/MessageDetailsContent.test.tsx b/ts/features/pn/components/__test__/MessageDetailsContent.test.tsx
new file mode 100644
index 00000000000..31e5a7c3603
--- /dev/null
+++ b/ts/features/pn/components/__test__/MessageDetailsContent.test.tsx
@@ -0,0 +1,15 @@
+import React from "react";
+import { render } from "@testing-library/react-native";
+import { MessageDetailsContent } from "../MessageDetailsContent";
+
+describe("MessageDetailsContent component", () => {
+ it("should match the snapshot when abstract is defined", () => {
+ const component = render();
+ expect(component.toJSON()).toMatchSnapshot();
+ });
+
+ it("should match the snapshot when abstract is not defined", () => {
+ const component = render();
+ expect(component.toJSON()).toMatchSnapshot();
+ });
+});
diff --git a/ts/features/pn/components/__test__/__snapshots__/MessageDetails.test.tsx.snap b/ts/features/pn/components/__test__/__snapshots__/MessageDetails.test.tsx.snap
new file mode 100644
index 00000000000..45785dc4e5f
--- /dev/null
+++ b/ts/features/pn/components/__test__/__snapshots__/MessageDetails.test.tsx.snap
@@ -0,0 +1,449 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`MessageDetails component should match the snapshot 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+ DUMMY_ROUTE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ######## subject ########
+
+
+
+ 01 Jan 2020, 00:00
+
+
+
+
+
+
+
+ ######## abstract ########
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/ts/features/pn/components/__test__/__snapshots__/MessageDetailsContent.test.tsx.snap b/ts/features/pn/components/__test__/__snapshots__/MessageDetailsContent.test.tsx.snap
new file mode 100644
index 00000000000..19f593ad32a
--- /dev/null
+++ b/ts/features/pn/components/__test__/__snapshots__/MessageDetailsContent.test.tsx.snap
@@ -0,0 +1,51 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`MessageDetailsContent component should match the snapshot when abstract is defined 1`] = `
+
+
+
+ abstract
+
+
+`;
+
+exports[`MessageDetailsContent component should match the snapshot when abstract is not defined 1`] = `null`;
diff --git a/ts/features/pn/navigation/navigator.tsx b/ts/features/pn/navigation/navigator.tsx
index 5b6a533ca60..d837d801971 100644
--- a/ts/features/pn/navigation/navigator.tsx
+++ b/ts/features/pn/navigation/navigator.tsx
@@ -2,30 +2,50 @@ import * as React from "react";
import { createStackNavigator } from "@react-navigation/stack";
import { isGestureEnabled } from "../../../utils/navigation";
import { MessageDetailsScreen } from "../screens/MessageDetailsScreen";
+import { LegacyMessageDetailsScreen } from "../screens/LegacyMessageDetailsScreen";
import { AttachmentPreviewScreen } from "../screens/AttachmentPreviewScreen";
import { PaidPaymentScreen } from "../screens/PaidPaymentScreen";
+import { useIOSelector } from "../../../store/hooks";
+import { isDesignSystemEnabledSelector } from "../../../store/reducers/persistedPreferences";
import { PnParamsList } from "./params";
import PN_ROUTES from "./routes";
const Stack = createStackNavigator();
-export const PnStackNavigator = () => (
-
-
-
-
-
-);
+export const PnStackNavigator = () => {
+ const isDesignSystemEnabled = useIOSelector(isDesignSystemEnabledSelector);
+
+ return (
+
+
+
+
+
+ );
+};
diff --git a/ts/features/pn/screens/LegacyMessageDetailsScreen.tsx b/ts/features/pn/screens/LegacyMessageDetailsScreen.tsx
new file mode 100644
index 00000000000..7d2ad7d08a2
--- /dev/null
+++ b/ts/features/pn/screens/LegacyMessageDetailsScreen.tsx
@@ -0,0 +1,132 @@
+import * as pot from "@pagopa/ts-commons/lib/pot";
+import { pipe } from "fp-ts/lib/function";
+import * as O from "fp-ts/lib/Option";
+import React from "react";
+import { SafeAreaView } from "react-native";
+import { useFocusEffect, useNavigation } from "@react-navigation/native";
+import { useStore } from "react-redux";
+import { IOStyles } from "../../../components/core/variables/IOStyles";
+import BaseScreenComponent from "../../../components/screens/BaseScreenComponent";
+import I18n from "../../../i18n";
+import { IOStackNavigationRouteProps } from "../../../navigation/params/AppParamsList";
+import { useIODispatch, useIOSelector } from "../../../store/hooks";
+import { serviceByIdSelector } from "../../../store/reducers/entities/services/servicesById";
+import { emptyContextualHelp } from "../../../utils/emptyContextualHelp";
+import { useOnFirstRender } from "../../../utils/hooks/useOnFirstRender";
+import { LegacyMessageDetails } from "../components/LegacyMessageDetails";
+import { PnParamsList } from "../navigation/params";
+import { pnMessageFromIdSelector } from "../store/reducers";
+import { cancelPreviousAttachmentDownload } from "../../messages/store/actions";
+import { profileFiscalCodeSelector } from "../../../store/reducers/profile";
+import {
+ containsF24FromPNMessagePot,
+ isCancelledFromPNMessagePot,
+ paymentsFromPNMessagePot
+} from "../utils";
+import { trackPNUxSuccess } from "../analytics";
+import { isStrictSome } from "../../../utils/pot";
+import {
+ cancelPaymentStatusTracking,
+ cancelQueuedPaymentUpdates,
+ clearSelectedPayment,
+ startPaymentStatusTracking,
+ updatePaymentForMessage
+} from "../store/actions";
+import { GlobalState } from "../../../store/reducers/types";
+import { selectedPaymentIdSelector } from "../store/reducers/payments";
+import { InfoScreenComponent } from "../../../components/infoScreen/InfoScreenComponent";
+import { renderInfoRasterImage } from "../../../components/infoScreen/imageRendering";
+import genericErrorIcon from "../../../../img/wallet/errors/generic-error-icon.png";
+
+export const LegacyMessageDetailsScreen = (
+ props: IOStackNavigationRouteProps
+): React.ReactElement => {
+ const { messageId, serviceId, firstTimeOpening } = props.route.params;
+
+ const dispatch = useIODispatch();
+ const navigation = useNavigation();
+
+ const service = pot.toUndefined(
+ useIOSelector(state => serviceByIdSelector(state, serviceId))
+ );
+
+ const currentFiscalCode = useIOSelector(profileFiscalCodeSelector);
+ const messagePot = useIOSelector(state =>
+ pnMessageFromIdSelector(state, messageId)
+ );
+ const payments = paymentsFromPNMessagePot(currentFiscalCode, messagePot);
+
+ const customGoBack = React.useCallback(() => {
+ dispatch(cancelPreviousAttachmentDownload());
+ dispatch(cancelQueuedPaymentUpdates());
+ dispatch(cancelPaymentStatusTracking());
+ navigation.goBack();
+ }, [dispatch, navigation]);
+
+ useOnFirstRender(() => {
+ dispatch(startPaymentStatusTracking(messageId));
+
+ if (isStrictSome(messagePot)) {
+ const paymentCount = payments?.length ?? 0;
+ const isCancelled = isCancelledFromPNMessagePot(messagePot);
+ const containsF24 = containsF24FromPNMessagePot(messagePot);
+
+ trackPNUxSuccess(
+ paymentCount,
+ firstTimeOpening,
+ isCancelled,
+ containsF24
+ );
+ }
+ });
+
+ const store = useStore();
+ useFocusEffect(
+ React.useCallback(() => {
+ const globalState = store.getState() as GlobalState;
+ const selectedPaymentId = selectedPaymentIdSelector(globalState);
+ if (selectedPaymentId) {
+ dispatch(clearSelectedPayment());
+ dispatch(
+ updatePaymentForMessage.request({
+ messageId,
+ paymentId: selectedPaymentId
+ })
+ );
+ }
+ }, [dispatch, messageId, store])
+ );
+
+ return (
+
+
+ {pipe(
+ messagePot,
+ pot.toOption,
+ O.flatten,
+ O.fold(
+ () => (
+
+ ),
+ message => (
+
+ )
+ )
+ )}
+
+
+ );
+};
diff --git a/ts/features/pn/screens/MessageDetailsScreen.tsx b/ts/features/pn/screens/MessageDetailsScreen.tsx
index bd84320d4a5..cc4ff8e8dbe 100644
--- a/ts/features/pn/screens/MessageDetailsScreen.tsx
+++ b/ts/features/pn/screens/MessageDetailsScreen.tsx
@@ -1,19 +1,19 @@
+import React, { useCallback } from "react";
+import {
+ RouteProp,
+ useFocusEffect,
+ useNavigation,
+ useRoute
+} from "@react-navigation/native";
+import { useStore } from "react-redux";
import * as pot from "@pagopa/ts-commons/lib/pot";
import { pipe } from "fp-ts/lib/function";
import * as O from "fp-ts/lib/Option";
-import React from "react";
-import { SafeAreaView } from "react-native";
-import { useFocusEffect, useNavigation } from "@react-navigation/native";
-import { useStore } from "react-redux";
-import { IOStyles } from "../../../components/core/variables/IOStyles";
-import BaseScreenComponent from "../../../components/screens/BaseScreenComponent";
import { ServiceId } from "../../../../definitions/backend/ServiceId";
import I18n from "../../../i18n";
-import { IOStackNavigationRouteProps } from "../../../navigation/params/AppParamsList";
import { useIODispatch, useIOSelector } from "../../../store/hooks";
import { UIMessageId } from "../../messages/types";
import { serviceByIdSelector } from "../../../store/reducers/entities/services/servicesById";
-import { emptyContextualHelp } from "../../../utils/emptyContextualHelp";
import { useOnFirstRender } from "../../../utils/hooks/useOnFirstRender";
import { MessageDetails } from "../components/MessageDetails";
import { PnParamsList } from "../navigation/params";
@@ -36,40 +36,48 @@ import {
} from "../store/actions";
import { GlobalState } from "../../../store/reducers/types";
import { selectedPaymentIdSelector } from "../store/reducers/payments";
-import { InfoScreenComponent } from "../../../components/infoScreen/InfoScreenComponent";
-import { renderInfoRasterImage } from "../../../components/infoScreen/imageRendering";
-import genericErrorIcon from "../../../../img/wallet/errors/generic-error-icon.png";
+import { useHeaderSecondLevel } from "../../../hooks/useHeaderSecondLevel";
+import { OperationResultScreenContent } from "../../../components/screens/OperationResultScreenContent";
-export type MessageDetailsScreenNavigationParams = Readonly<{
+export type MessageDetailsScreenNavigationParams = {
messageId: UIMessageId;
serviceId: ServiceId;
firstTimeOpening: boolean;
-}>;
+};
-export const MessageDetailsScreen = (
- props: IOStackNavigationRouteProps
-): React.ReactElement => {
- const { messageId, serviceId, firstTimeOpening } = props.route.params;
+type MessageDetailsRouteProps = RouteProp<
+ PnParamsList,
+ "PN_ROUTES_MESSAGE_DETAILS"
+>;
+export const MessageDetailsScreen = () => {
const dispatch = useIODispatch();
const navigation = useNavigation();
+ const route = useRoute();
+
+ const { messageId, serviceId, firstTimeOpening } = route.params;
const service = pot.toUndefined(
useIOSelector(state => serviceByIdSelector(state, serviceId))
);
-
const currentFiscalCode = useIOSelector(profileFiscalCodeSelector);
const messagePot = useIOSelector(state =>
pnMessageFromIdSelector(state, messageId)
);
const payments = paymentsFromPNMessagePot(currentFiscalCode, messagePot);
- const customGoBack = React.useCallback(() => {
+ const goBack = useCallback(() => {
dispatch(cancelPreviousAttachmentDownload());
dispatch(cancelQueuedPaymentUpdates());
dispatch(cancelPaymentStatusTracking());
navigation.goBack();
- }, [dispatch, navigation]);
+ }, []);
+
+ useHeaderSecondLevel({
+ title: "",
+ goBack,
+ supportRequest: true
+ });
useOnFirstRender(() => {
dispatch(startPaymentStatusTracking(messageId));
@@ -90,7 +98,7 @@ export const MessageDetailsScreen = (
const store = useStore();
useFocusEffect(
- React.useCallback(() => {
+ useCallback(() => {
const globalState = store.getState() as GlobalState;
const selectedPaymentId = selectedPaymentIdSelector(globalState);
if (selectedPaymentId) {
@@ -106,35 +114,29 @@ export const MessageDetailsScreen = (
);
return (
-
-
- {pipe(
- messagePot,
- pot.toOption,
- O.flatten,
- O.fold(
- () => (
-
- ),
- message => (
-
- )
+ <>
+ {pipe(
+ messagePot,
+ pot.toOption,
+ O.flatten,
+ O.fold(
+ () => (
+
+ ),
+ message => (
+
)
- )}
-
-
+ )
+ )}
+ >
);
};
diff --git a/ts/features/pn/screens/__test__/MessageDetailsScreen.test.tsx b/ts/features/pn/screens/__test__/MessageDetailsScreen.test.tsx
new file mode 100644
index 00000000000..0aa4268dca0
--- /dev/null
+++ b/ts/features/pn/screens/__test__/MessageDetailsScreen.test.tsx
@@ -0,0 +1,83 @@
+import configureMockStore from "redux-mock-store";
+import { Action, Store } from "redux";
+import PN_ROUTES from "../../navigation/routes";
+import { GlobalState } from "../../../../store/reducers/types";
+import { appReducer } from "../../../../store/reducers";
+import { MessageDetailsScreen } from "../MessageDetailsScreen";
+import { renderScreenWithNavigationStoreContext } from "../../../../utils/testWrapper";
+import { reproduceSequence } from "../../../../utils/tests";
+import {
+ loadMessageById,
+ loadMessageDetails,
+ loadThirdPartyMessage
+} from "../../../messages/store/actions";
+import {
+ toUIMessage,
+ toUIMessageDetails
+} from "../../../messages/store/reducers/transformers";
+import { message_1 } from "../../../messages/__mocks__/message";
+import { loadServiceDetail } from "../../../../store/actions/services";
+import { service_1 } from "../../../messages/__mocks__/messages";
+import { UIMessageId } from "../../../messages/types";
+import { applicationChangeState } from "../../../../store/actions/application";
+import { thirdPartyMessage } from "../../__mocks__/message";
+
+describe("MessageDetailsScreen", () => {
+ it("should match the snapshot when there is an error", () => {
+ const sequenceOfActions: ReadonlyArray = [
+ applicationChangeState("active")
+ ];
+
+ const state: GlobalState = reproduceSequence(
+ {} as GlobalState,
+ appReducer,
+ sequenceOfActions
+ );
+ const mockStore = configureMockStore();
+ const store: Store = mockStore(state);
+
+ const { component } = renderComponent(store);
+ expect(component).toMatchSnapshot();
+ });
+
+ it("should match the snapshot when everything went fine", () => {
+ const sequenceOfActions: ReadonlyArray = [
+ applicationChangeState("active"),
+ loadMessageById.success(toUIMessage(message_1)),
+ loadServiceDetail.success(service_1),
+ loadMessageDetails.success(toUIMessageDetails(message_1)),
+ loadThirdPartyMessage.success({
+ id: message_1.id as UIMessageId,
+ content: thirdPartyMessage
+ })
+ ];
+
+ const state: GlobalState = reproduceSequence(
+ {} as GlobalState,
+ appReducer,
+ sequenceOfActions
+ );
+ const mockStore = configureMockStore();
+ const store: Store = mockStore(state);
+
+ const { component } = renderComponent(store);
+ expect(component).toMatchSnapshot();
+ });
+});
+
+const renderComponent = (store: Store) => {
+ const { id, sender_service_id } = message_1;
+
+ return {
+ component: renderScreenWithNavigationStoreContext(
+ MessageDetailsScreen,
+ PN_ROUTES.MESSAGE_DETAILS,
+ {
+ firstTimeOpening: false,
+ messageId: id,
+ serviceId: sender_service_id
+ },
+ store
+ )
+ };
+};
diff --git a/ts/features/pn/screens/__test__/__snapshots__/MessageDetailsScreen.test.tsx.snap b/ts/features/pn/screens/__test__/__snapshots__/MessageDetailsScreen.test.tsx.snap
new file mode 100644
index 00000000000..48edcca56d3
--- /dev/null
+++ b/ts/features/pn/screens/__test__/__snapshots__/MessageDetailsScreen.test.tsx.snap
@@ -0,0 +1,1372 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`MessageDetailsScreen should match the snapshot when everything went fine 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ######## subject ########
+
+
+
+ 01 Jan 2020, 00:00
+
+
+
+
+
+
+
+ ######## abstract ########
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[`MessageDetailsScreen should match the snapshot when there is an error 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Qualcosa è andato storto
+
+
+
+ Non è stato possibile recuperare i dettagli del tuo messaggio. Riprova per favore
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/ts/features/pn/store/types/transformers.ts b/ts/features/pn/store/types/transformers.ts
index e89ffc030b4..3b0c5269074 100644
--- a/ts/features/pn/store/types/transformers.ts
+++ b/ts/features/pn/store/types/transformers.ts
@@ -15,6 +15,7 @@ export const toPNMessage = (
O.chainNullableK(message => message.details),
O.map(details => ({
...details,
+ created_at: messageFromApi.created_at,
attachments: pipe(
attachmentsFromThirdPartyMessage(messageFromApi),
O.toUndefined
diff --git a/ts/features/pn/store/types/types.ts b/ts/features/pn/store/types/types.ts
index 525cce459d3..192e7d9652a 100644
--- a/ts/features/pn/store/types/types.ts
+++ b/ts/features/pn/store/types/types.ts
@@ -1,7 +1,7 @@
import { IOReceivedNotification } from "../../../../../definitions/pn/IOReceivedNotification";
import { UIAttachment } from "../../../messages/types";
-export type PNMessage = IOReceivedNotification &
- Readonly<{
- attachments?: ReadonlyArray;
- }>;
+export type PNMessage = IOReceivedNotification & {
+ created_at: Date;
+ attachments?: ReadonlyArray;
+};