From 7cced1c7d07996b5d85ab684ce1371e1c470fe26 Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Wed, 24 Jan 2024 11:28:56 +0100 Subject: [PATCH 01/17] add title, date and content --- .../MessageDetail/MessageDetailHeader.tsx | 48 +++++ .../pn/components/LegacyMessageDetails.tsx | 193 +++++++++++++++++ .../LegacyMessageDetailsContent.tsx | 31 +++ ts/features/pn/components/MessageDetails.tsx | 200 ++---------------- .../pn/components/MessageDetailsContent.tsx | 44 ++-- .../__test__/LegacyMessageDetails.test.tsx | 187 ++++++++++++++++ ts/features/pn/navigation/navigator.tsx | 51 +++-- .../pn/screens/LegacyMessageDetailsScreen.tsx | 140 ++++++++++++ .../pn/screens/MessageDetailsScreen.tsx | 35 +-- ts/features/pn/store/types/transformers.ts | 1 + ts/features/pn/store/types/types.ts | 8 +- 11 files changed, 690 insertions(+), 248 deletions(-) create mode 100644 ts/features/messages/components/MessageDetail/MessageDetailHeader.tsx create mode 100644 ts/features/pn/components/LegacyMessageDetails.tsx create mode 100644 ts/features/pn/components/LegacyMessageDetailsContent.tsx create mode 100644 ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx create mode 100644 ts/features/pn/screens/LegacyMessageDetailsScreen.tsx 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/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..703d01793ac 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 React from "react"; 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 { 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; + messageId: UIMessageId; 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 && ( - - )} - - - - ); }; + +export const MessageDetails = ({ message, service }: MessageDetailsProps) => ( + + + + +); diff --git a/ts/features/pn/components/MessageDetailsContent.tsx b/ts/features/pn/components/MessageDetailsContent.tsx index 21b33cfef0c..6bae2e6e2e6 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 | undefined; +}; -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..d1e7fb14f64 --- /dev/null +++ b/ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx @@ -0,0 +1,187 @@ +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 => ({ + 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/navigation/navigator.tsx b/ts/features/pn/navigation/navigator.tsx index 5b6a533ca60..df23de6cdef 100644 --- a/ts/features/pn/navigation/navigator.tsx +++ b/ts/features/pn/navigation/navigator.tsx @@ -2,30 +2,41 @@ 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..012f077524f --- /dev/null +++ b/ts/features/pn/screens/LegacyMessageDetailsScreen.tsx @@ -0,0 +1,140 @@ +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 { 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 type MessageDetailsScreenNavigationParams = Readonly<{ + messageId: UIMessageId; + serviceId: ServiceId; + firstTimeOpening: boolean; +}>; + +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..24c6b8a21cf 100644 --- a/ts/features/pn/screens/MessageDetailsScreen.tsx +++ b/ts/features/pn/screens/MessageDetailsScreen.tsx @@ -1,15 +1,19 @@ +import React, { useCallback } from "react"; +import { SafeAreaView } from "react-native"; +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"; @@ -40,31 +44,34 @@ import { InfoScreenComponent } from "../../../components/infoScreen/InfoScreenCo import { renderInfoRasterImage } from "../../../components/infoScreen/imageRendering"; import genericErrorIcon from "../../../../img/wallet/errors/generic-error-icon.png"; -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 customGoBack = useCallback(() => { dispatch(cancelPreviousAttachmentDownload()); dispatch(cancelQueuedPaymentUpdates()); dispatch(cancelPaymentStatusTracking()); @@ -90,7 +97,7 @@ export const MessageDetailsScreen = ( const store = useStore(); useFocusEffect( - React.useCallback(() => { + useCallback(() => { const globalState = store.getState() as GlobalState; const selectedPaymentId = selectedPaymentIdSelector(globalState); if (selectedPaymentId) { 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; +}; From 239d8d3f9b11c0a7900a16bb406f1e56afba8a7d Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Wed, 24 Jan 2024 11:52:32 +0100 Subject: [PATCH 02/17] add tests --- .../__tests__/MessageDetailHeader.test.tsx | 15 ++ .../MessageDetailHeader.test.tsx.snap | 96 +++++++++ .../__test__/LegacyMessageDetails.test.tsx | 1 + .../__test__/MessageDetails.test.tsx | 187 ------------------ .../__test__/MessageDetailsContent.test.tsx | 10 + .../MessageDetailsContent.test.tsx.snap | 49 +++++ 6 files changed, 171 insertions(+), 187 deletions(-) create mode 100644 ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx create mode 100644 ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap delete mode 100644 ts/features/pn/components/__test__/MessageDetails.test.tsx create mode 100644 ts/features/pn/components/__test__/MessageDetailsContent.test.tsx create mode 100644 ts/features/pn/components/__test__/__snapshots__/MessageDetailsContent.test.tsx.snap 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..dcb7edbcc7e --- /dev/null +++ b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx @@ -0,0 +1,15 @@ +import React, { ComponentProps } from "react"; +import { render } from "@testing-library/react-native"; +import { MessageDetailHeader } from "../MessageDetailHeader"; + +const defaultProps: ComponentProps = { + subject: "Subject", + createdAt: new Date() +}; + +describe("MessageDetailHeader component", () => { + it("should match the snapshot with default 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..10f67728de5 --- /dev/null +++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap @@ -0,0 +1,96 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MessageDetailHeader component should match the snapshot with default props 1`] = ` + + + Subject + + + + 24 Jan 2024, 10:51 + + + + +`; diff --git a/ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx b/ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx index d1e7fb14f64..a7ec40d4d30 100644 --- a/ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx +++ b/ts/features/pn/components/__test__/LegacyMessageDetails.test.tsx @@ -127,6 +127,7 @@ describe("LegacyMessageDetails component", () => { 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", diff --git a/ts/features/pn/components/__test__/MessageDetails.test.tsx b/ts/features/pn/components/__test__/MessageDetails.test.tsx deleted file mode 100644 index 11a7c0e7b65..00000000000 --- a/ts/features/pn/components/__test__/MessageDetails.test.tsx +++ /dev/null @@ -1,187 +0,0 @@ -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 { 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"; - -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("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", () => { - 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 => ({ - 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__/MessageDetailsContent.test.tsx b/ts/features/pn/components/__test__/MessageDetailsContent.test.tsx new file mode 100644 index 00000000000..226c28a2c50 --- /dev/null +++ b/ts/features/pn/components/__test__/MessageDetailsContent.test.tsx @@ -0,0 +1,10 @@ +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(); + }); +}); 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..eae994ed610 --- /dev/null +++ b/ts/features/pn/components/__test__/__snapshots__/MessageDetailsContent.test.tsx.snap @@ -0,0 +1,49 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`MessageDetailsContent component should match the snapshot when abstract is defined 1`] = ` + + + + abstract + + +`; From cd149efc3fb3fa065f64a3e5b47441ee7a54becd Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Wed, 24 Jan 2024 11:55:37 +0100 Subject: [PATCH 03/17] change text color --- ts/features/pn/components/MessageDetailsContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/features/pn/components/MessageDetailsContent.tsx b/ts/features/pn/components/MessageDetailsContent.tsx index 6bae2e6e2e6..5b8b8dcd624 100644 --- a/ts/features/pn/components/MessageDetailsContent.tsx +++ b/ts/features/pn/components/MessageDetailsContent.tsx @@ -15,7 +15,7 @@ export const MessageDetailsContent = ({ return ( - {abstract} + {abstract} ); }; From bcfcb8ea4635fd95da3b264208124993e045512b Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Wed, 24 Jan 2024 12:04:19 +0100 Subject: [PATCH 04/17] Update message timestamp and color --- .../__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap | 2 +- .../__snapshots__/MessageDetailsContent.test.tsx.snap | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 index 10f67728de5..aa92ea0f50f 100644 --- a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap +++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap @@ -75,7 +75,7 @@ exports[`MessageDetailHeader component should match the snapshot with default pr } weight="Bold" > - 24 Jan 2024, 10:51 + 24 Jan 2024, 11:01 Date: Wed, 24 Jan 2024 14:53:40 +0100 Subject: [PATCH 05/17] update snap --- .../__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index aa92ea0f50f..75594282955 100644 --- a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap +++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap @@ -75,7 +75,7 @@ exports[`MessageDetailHeader component should match the snapshot with default pr } weight="Bold" > - 24 Jan 2024, 11:01 + 24 Jan 2024, 13:52 Date: Wed, 24 Jan 2024 15:16:51 +0100 Subject: [PATCH 06/17] fix test --- .../MessageDetail/__tests__/MessageDetailHeader.test.tsx | 2 +- .../__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx index dcb7edbcc7e..be739559c74 100644 --- a/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx +++ b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx @@ -4,7 +4,7 @@ import { MessageDetailHeader } from "../MessageDetailHeader"; const defaultProps: ComponentProps = { subject: "Subject", - createdAt: new Date() + createdAt: new Date("2021-10-18T16:00:30.541Z") }; describe("MessageDetailHeader component", () => { 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 index 75594282955..c5b0788e14d 100644 --- a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap +++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap @@ -75,7 +75,7 @@ exports[`MessageDetailHeader component should match the snapshot with default pr } weight="Bold" > - 24 Jan 2024, 13:52 + 18 Oct 2021, 16:00 Date: Thu, 25 Jan 2024 11:18:51 +0100 Subject: [PATCH 07/17] add tags --- locales/en/index.yml | 2 + locales/it/index.yml | 2 + ts/features/pn/components/MessageDetails.tsx | 57 ++++++++--- .../__test__/MessageDetails.test.tsx | 94 +++++++++++++++++++ 4 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 ts/features/pn/components/__test__/MessageDetails.test.tsx diff --git a/locales/en/index.yml b/locales/en/index.yml index e0e59a0642b..08604206afe 100644 --- a/locales/en/index.yml +++ b/locales/en/index.yml @@ -3116,6 +3116,8 @@ features: activated: "Grazie, il servizio è attivo!" error: "Si è verificato un errore con la tua richiesta" details: + badge: + legalValue: "Legal value" title: Dettaglio del messaggio noticeCode: "Codice avviso" loadError: diff --git a/locales/it/index.yml b/locales/it/index.yml index f5a8d5b56b3..17a6b9c3004 100644 --- a/locales/it/index.yml +++ b/locales/it/index.yml @@ -3116,6 +3116,8 @@ features: activated: "Grazie, il servizio è attivo!" error: "Si è verificato un errore con la tua richiesta" details: + badge: + legalValue: "Valore legale" title: Dettaglio del messaggio noticeCode: "Codice avviso" loadError: diff --git a/ts/features/pn/components/MessageDetails.tsx b/ts/features/pn/components/MessageDetails.tsx index 703d01793ac..e9f317a8836 100644 --- a/ts/features/pn/components/MessageDetails.tsx +++ b/ts/features/pn/components/MessageDetails.tsx @@ -1,10 +1,18 @@ import React from "react"; +import { View } from "react-native"; import { ScrollView } from "react-native-gesture-handler"; +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 { HSpacer, IOStyles, Tag, VSpacer } from "@pagopa/io-app-design-system"; import { ServicePublic } from "../../../../definitions/backend/ServicePublic"; -import { UIMessageId } from "../../messages/types"; +import { UIAttachment, UIMessageId } from "../../messages/types"; import { PNMessage } from "../store/types/types"; import { NotificationPaymentInfo } from "../../../../definitions/pn/NotificationPaymentInfo"; import { MessageDetailHeader } from "../../messages/components/MessageDetail/MessageDetailHeader"; +import { ATTACHMENT_CATEGORY } from "../../messages/types/attachmentCategory"; +import I18n from "../../../i18n"; import { MessageDetailsContent } from "./MessageDetailsContent"; type MessageDetailsProps = { @@ -14,14 +22,39 @@ type MessageDetailsProps = { payments: ReadonlyArray | undefined; }; -export const MessageDetails = ({ message, service }: MessageDetailsProps) => ( - - - - -); +export const MessageDetails = ({ message, service }: MessageDetailsProps) => { + const partitionedAttachments = pipe( + message.attachments, + O.fromNullable, + O.getOrElse>(() => []), + RA.partition(attachment => attachment.category === ATTACHMENT_CATEGORY.F24) + ); + + const attachmentList = SEP.left(partitionedAttachments); + + return ( + + + + + {attachmentList.length > 0 ? ( + <> + + + + ) : null} + + + + + + ); +}; diff --git a/ts/features/pn/components/__test__/MessageDetails.test.tsx b/ts/features/pn/components/__test__/MessageDetails.test.tsx new file mode 100644 index 00000000000..bac4a3d9a1d --- /dev/null +++ b/ts/features/pn/components/__test__/MessageDetails.test.tsx @@ -0,0 +1,94 @@ +import React from "react"; +import { createStore } from "redux"; +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 { NotificationRecipient } from "../../../../../definitions/pn/NotificationRecipient"; +import { ATTACHMENT_CATEGORY } from "../../../messages/types/attachmentCategory"; +import I18n from "../../../../i18n"; + +describe("MessageDetails component", () => { + it("should display the legalMessage tag", () => { + const { component } = renderComponent( + generateComponentProperties(pnMessage) + ); + expect( + component.queryByText(I18n.t("features.pn.details.badge.legalValue")) + ).not.toBeNull(); + }); + + it("should display the attachment tag if there are attachments", () => { + const { component } = renderComponent( + generateComponentProperties(pnMessage) + ); + expect(component.queryByTestId("attachment-tag")).not.toBeNull(); + }); +}); + +const messageId = "00000000000000000000000004" as UIMessageId; +const fiscalCode = "AAABBB00A00A000A"; +const pnMessage: 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: fiscalCode, + denomination: "AaAaAa BbBbBb", + payment: { + noticeCode: "026773337463073118", + creditorTaxId: "00000000009" + } + } + ] as Array, + attachments: [ + { + messageId, + id: "1", + displayName: "A First Attachment", + contentType: "application/pdf", + category: ATTACHMENT_CATEGORY.DOCUMENT, + resourceUrl: { href: "/resource/attachment1.pdf" } + }, + { + messageId, + id: "2", + displayName: "A Second Attachment", + contentType: "application/pdf", + category: ATTACHMENT_CATEGORY.DOCUMENT, + resourceUrl: { href: "/resource/attachment2.pdf" } + } + ] as Array +}; +const generateComponentProperties = (pnMessage: PNMessage) => ({ + messageId, + payments: undefined, + 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 + }; +}; From 6602fe8d5e0d3193bab43d42d9bf83b9034f2c23 Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Mon, 29 Jan 2024 18:11:22 +0100 Subject: [PATCH 08/17] update tests --- ts/features/pn/__mocks__/message.ts | 35 ++- .../__test__/MessageDetails.test.tsx | 29 ++- .../MessageDetails.test.tsx.snap | 210 +++++++++++++++++- .../MessageDetailsScreen.test.tsx.snap | 208 +++++++++++++++++ 4 files changed, 460 insertions(+), 22 deletions(-) diff --git a/ts/features/pn/__mocks__/message.ts b/ts/features/pn/__mocks__/message.ts index 6bfc8eeaccb..0bea75bc58e 100644 --- a/ts/features/pn/__mocks__/message.ts +++ b/ts/features/pn/__mocks__/message.ts @@ -1,4 +1,5 @@ import { ThirdPartyMessageWithContent } from "../../../../definitions/backend/ThirdPartyMessageWithContent"; +import { ThirdPartyAttachment } from "../../../../definitions/pn/ThirdPartyAttachment"; import { message_1 } from "../../messages/__mocks__/message"; import { ATTACHMENT_CATEGORY } from "../../messages/types/attachmentCategory"; @@ -6,26 +7,24 @@ export const thirdPartyMessage: ThirdPartyMessageWithContent = { ...message_1, created_at: new Date("2020-01-01T00:00:00.000Z"), third_party_message: { + attachments: [ + { + id: "1", + name: "A First Attachment", + content_type: "application/pdf", + category: ATTACHMENT_CATEGORY.DOCUMENT, + url: "/resource/attachment1.pdf" + }, + { + id: "2", + name: "A Second Attachment", + content_type: "application/pdf", + category: ATTACHMENT_CATEGORY.DOCUMENT, + url: "/resource/attachment2.pdf" + } + ] as Array, 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: [ diff --git a/ts/features/pn/components/__test__/MessageDetails.test.tsx b/ts/features/pn/components/__test__/MessageDetails.test.tsx index b6c8687b84c..22915b1a2af 100644 --- a/ts/features/pn/components/__test__/MessageDetails.test.tsx +++ b/ts/features/pn/components/__test__/MessageDetails.test.tsx @@ -11,19 +11,42 @@ import { PNMessage } from "../../store/types/types"; import { thirdPartyMessage } from "../../__mocks__/message"; import { toPNMessage } from "../../store/types/transformers"; import { UIMessageId } from "../../../messages/types"; +import I18n from "../../../../i18n"; -const pnMessage = pipe(thirdPartyMessage, toPNMessage, O.toUndefined); +const pnMessage = pipe(thirdPartyMessage, toPNMessage, O.toUndefined)!; describe("MessageDetails component", () => { - it("should match the snapshot", () => { + it("should match the snapshot with default props", () => { const { component } = renderComponent( generateComponentProperties( thirdPartyMessage.id as UIMessageId, - pnMessage! + pnMessage ) ); expect(component).toMatchSnapshot(); }); + + it("should display the legalMessage tag", () => { + const { component } = renderComponent( + generateComponentProperties( + thirdPartyMessage.id as UIMessageId, + pnMessage + ) + ); + expect( + component.queryByText(I18n.t("features.pn.details.badge.legalValue")) + ).not.toBeNull(); + }); + + it("should display the attachment tag if there are attachments", () => { + const { component } = renderComponent( + generateComponentProperties( + thirdPartyMessage.id as UIMessageId, + pnMessage + ) + ); + expect(component.queryByTestId("attachment-tag")).not.toBeNull(); + }); }); const generateComponentProperties = ( diff --git a/ts/features/pn/components/__test__/__snapshots__/MessageDetails.test.tsx.snap b/ts/features/pn/components/__test__/__snapshots__/MessageDetails.test.tsx.snap index 45785dc4e5f..ec8a030d60f 100644 --- a/ts/features/pn/components/__test__/__snapshots__/MessageDetails.test.tsx.snap +++ b/ts/features/pn/components/__test__/__snapshots__/MessageDetails.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`MessageDetails component should match the snapshot 1`] = ` +exports[`MessageDetails component should match the snapshot with default props 1`] = ` + + + + + + + + + + + + Legal value + + + + + + + + + + + + + + + + + + + + + + + + + + Legal value + + + + + + + + + + + + + + Date: Mon, 29 Jan 2024 18:16:34 +0100 Subject: [PATCH 09/17] applied suggestion --- ts/features/pn/components/MessageDetails.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ts/features/pn/components/MessageDetails.tsx b/ts/features/pn/components/MessageDetails.tsx index fabade4e38a..bb5ce071485 100644 --- a/ts/features/pn/components/MessageDetails.tsx +++ b/ts/features/pn/components/MessageDetails.tsx @@ -44,12 +44,12 @@ export const MessageDetails = ({ message, service }: MessageDetailsProps) => { text={I18n.t("features.pn.details.badge.legalValue")} variant="legalMessage" /> - {attachmentList.length > 0 ? ( + {attachmentList.length > 0 && ( <> - ) : null} + )} From 46c9b1ad24a591f63009b500feb0633456f203e3 Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Mon, 29 Jan 2024 19:04:47 +0100 Subject: [PATCH 10/17] update test --- .../pn/components/__test__/MessageDetails.test.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ts/features/pn/components/__test__/MessageDetails.test.tsx b/ts/features/pn/components/__test__/MessageDetails.test.tsx index 22915b1a2af..a87f86291be 100644 --- a/ts/features/pn/components/__test__/MessageDetails.test.tsx +++ b/ts/features/pn/components/__test__/MessageDetails.test.tsx @@ -47,6 +47,16 @@ describe("MessageDetails component", () => { ); expect(component.queryByTestId("attachment-tag")).not.toBeNull(); }); + + it("should NOT display the attachment tag if there are no attachments", () => { + const { component } = renderComponent( + generateComponentProperties(thirdPartyMessage.id as UIMessageId, { + ...pnMessage, + attachments: [] + }) + ); + expect(component.queryByTestId("attachment-tag")).toBeNull(); + }); }); const generateComponentProperties = ( From beb44698e8fcdf4a68c4b80a27865c1ca98538ed Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Tue, 30 Jan 2024 11:47:37 +0100 Subject: [PATCH 11/17] add OrganizationHeader component --- .../MessageDetail/MessageDetailHeader.tsx | 33 +++-- .../MessageDetail/OrganizationHeader.tsx | 51 +++++++ .../__tests__/MessageDetailHeader.test.tsx | 14 +- .../__tests__/OrganizationHeader.test.tsx | 18 +++ .../MessageDetailHeader.test.tsx.snap | 132 ++++++++++++++++++ .../OrganizationHeader.test.tsx.snap | 128 +++++++++++++++++ .../MessageDetailsScreen.test.tsx.snap | 132 ++++++++++++++++++ ts/utils/services.ts | 13 ++ 8 files changed, 509 insertions(+), 12 deletions(-) create mode 100644 ts/features/messages/components/MessageDetail/OrganizationHeader.tsx create mode 100644 ts/features/messages/components/MessageDetail/__tests__/OrganizationHeader.test.tsx create mode 100644 ts/features/messages/components/MessageDetail/__tests__/__snapshots__/OrganizationHeader.test.tsx.snap diff --git a/ts/features/messages/components/MessageDetail/MessageDetailHeader.tsx b/ts/features/messages/components/MessageDetail/MessageDetailHeader.tsx index 849cf89df7c..f541fcc9f37 100644 --- a/ts/features/messages/components/MessageDetail/MessageDetailHeader.tsx +++ b/ts/features/messages/components/MessageDetail/MessageDetailHeader.tsx @@ -1,9 +1,16 @@ -import { Divider, H3, LabelSmall, VSpacer } from "@pagopa/io-app-design-system"; import React, { PropsWithChildren } from "react"; -import { StyleSheet, View } from "react-native"; +import { + ContentWrapper, + Divider, + H3, + LabelSmall, + VSpacer +} from "@pagopa/io-app-design-system"; import { localeDateFormat } from "../../../../utils/locale"; import I18n from "../../../../i18n"; import { ServicePublic } from "../../../../../definitions/backend/ServicePublic"; +import { logoForService } from "../../../../utils/services"; +import { OrganizationHeader } from "./OrganizationHeader"; export type MessageDetailHeaderProps = PropsWithChildren<{ createdAt: Date; @@ -12,12 +19,6 @@ export type MessageDetailHeaderProps = PropsWithChildren<{ service?: ServicePublic; }>; -const styles = StyleSheet.create({ - header: { - paddingHorizontal: 24 - } -}); - const MessageHeaderContent = ({ subject, createdAt @@ -36,13 +37,23 @@ const MessageHeaderContent = ({ export const MessageDetailHeader = ({ children, + service, ...rest }: MessageDetailHeaderProps) => ( - + {children} - {/* TODO: Add MessageHeaderService */} - + {service && ( + <> + + + + )} + ); diff --git a/ts/features/messages/components/MessageDetail/OrganizationHeader.tsx b/ts/features/messages/components/MessageDetail/OrganizationHeader.tsx new file mode 100644 index 00000000000..d919bc1016d --- /dev/null +++ b/ts/features/messages/components/MessageDetail/OrganizationHeader.tsx @@ -0,0 +1,51 @@ +import React from "react"; +import { ImageURISource, StyleSheet, View } from "react-native"; +import { + Avatar, + IOSpacingScale, + IOStyles, + LabelSmall +} from "@pagopa/io-app-design-system"; + +export type OrganizationHeaderProps = { + organizationName: string; + serviceName: string; + logoUri: ImageURISource; + accessibilityLabel?: string; +}; + +const ITEM_PADDING_VERTICAL: IOSpacingScale = 6; +const AVATAR_MARGIN_LEFT: IOSpacingScale = 16; + +const styles = StyleSheet.create({ + item: { + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + paddingVertical: ITEM_PADDING_VERTICAL + }, + itemAvatar: { + marginLeft: AVATAR_MARGIN_LEFT + } +}); + +export const OrganizationHeader = ({ + logoUri, + organizationName, + serviceName, + ...rest +}: OrganizationHeaderProps) => ( + + + + {organizationName} + + + {serviceName} + + + + + + +); diff --git a/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx index 5539b0d3fdc..145c99ef552 100644 --- a/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx +++ b/ts/features/messages/components/MessageDetail/__tests__/MessageDetailHeader.test.tsx @@ -26,10 +26,22 @@ describe("MessageDetailHeader component", () => { const component = render( ); expect(component.toJSON()).toMatchSnapshot(); }); + + it("should render the organization info when the service is defined", () => { + const component = render( + + ); + expect(component.queryByText(service.organization_name)).not.toBeNull(); + expect(component.queryByText(service.service_name)).not.toBeNull(); + }); }); diff --git a/ts/features/messages/components/MessageDetail/__tests__/OrganizationHeader.test.tsx b/ts/features/messages/components/MessageDetail/__tests__/OrganizationHeader.test.tsx new file mode 100644 index 00000000000..2115f08ef86 --- /dev/null +++ b/ts/features/messages/components/MessageDetail/__tests__/OrganizationHeader.test.tsx @@ -0,0 +1,18 @@ +import React from "react"; +import { render } from "@testing-library/react-native"; +import { OrganizationHeader } from "../OrganizationHeader"; + +describe("OrganizationHeader component", () => { + it("should match the snapshot", () => { + 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 index d1597d22480..5fb01ad514d 100644 --- a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap +++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap @@ -92,6 +92,138 @@ exports[`MessageDetailHeader component should match the snapshot with all props } } /> + + + + Organization foo + + + health + + + + + + + + + `; diff --git a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/OrganizationHeader.test.tsx.snap b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/OrganizationHeader.test.tsx.snap new file mode 100644 index 00000000000..6bc59b677e6 --- /dev/null +++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/OrganizationHeader.test.tsx.snap @@ -0,0 +1,128 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`OrganizationHeader component should match the snapshot 1`] = ` + + + + Universala Esperanto-Asocio + + + Avviso + + + + + + + + +`; diff --git a/ts/features/pn/screens/__test__/__snapshots__/MessageDetailsScreen.test.tsx.snap b/ts/features/pn/screens/__test__/__snapshots__/MessageDetailsScreen.test.tsx.snap index 9a3f4a76a2f..d8f6acddb0a 100644 --- a/ts/features/pn/screens/__test__/__snapshots__/MessageDetailsScreen.test.tsx.snap +++ b/ts/features/pn/screens/__test__/__snapshots__/MessageDetailsScreen.test.tsx.snap @@ -782,6 +782,138 @@ exports[`MessageDetailsScreen should match the snapshot when everything went fin } } /> + + + + Ċentru tas-Saħħa + + + health + + + + + + + + + ({ + uri: `${logosRepoUrl}/services/${service.service_id.toLowerCase()}.png` +}); + /** * Returns an array of ImageURISource pointing to possible logos for the * provided service. From 6f123e1f7fc9cdaf6aeeff265b98fef949614b72 Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Wed, 31 Jan 2024 16:56:05 +0100 Subject: [PATCH 12/17] added missing deps to useCallback --- ts/features/pn/screens/MessageDetailsScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/features/pn/screens/MessageDetailsScreen.tsx b/ts/features/pn/screens/MessageDetailsScreen.tsx index cc4ff8e8dbe..dc68816b0b1 100644 --- a/ts/features/pn/screens/MessageDetailsScreen.tsx +++ b/ts/features/pn/screens/MessageDetailsScreen.tsx @@ -71,7 +71,7 @@ export const MessageDetailsScreen = () => { dispatch(cancelQueuedPaymentUpdates()); dispatch(cancelPaymentStatusTracking()); navigation.goBack(); - }, []); + }, [dispatch, navigation]); useHeaderSecondLevel({ title: "", From f848965b31e9a0e725eb8634458b2a39696bba5b Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Wed, 31 Jan 2024 17:18:00 +0100 Subject: [PATCH 13/17] removed useless prop --- .../components/MessageDetail/OrganizationHeader.tsx | 6 ++---- .../__snapshots__/OrganizationHeader.test.tsx.snap | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/ts/features/messages/components/MessageDetail/OrganizationHeader.tsx b/ts/features/messages/components/MessageDetail/OrganizationHeader.tsx index d919bc1016d..43537c53909 100644 --- a/ts/features/messages/components/MessageDetail/OrganizationHeader.tsx +++ b/ts/features/messages/components/MessageDetail/OrganizationHeader.tsx @@ -11,7 +11,6 @@ export type OrganizationHeaderProps = { organizationName: string; serviceName: string; logoUri: ImageURISource; - accessibilityLabel?: string; }; const ITEM_PADDING_VERTICAL: IOSpacingScale = 6; @@ -32,10 +31,9 @@ const styles = StyleSheet.create({ export const OrganizationHeader = ({ logoUri, organizationName, - serviceName, - ...rest + serviceName }: OrganizationHeaderProps) => ( - + {organizationName} diff --git a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/OrganizationHeader.test.tsx.snap b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/OrganizationHeader.test.tsx.snap index 6bc59b677e6..64937ad016d 100644 --- a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/OrganizationHeader.test.tsx.snap +++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/OrganizationHeader.test.tsx.snap @@ -2,7 +2,6 @@ exports[`OrganizationHeader component should match the snapshot 1`] = ` Date: Wed, 31 Jan 2024 17:32:33 +0100 Subject: [PATCH 14/17] update snap --- .../__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap | 1 - 1 file changed, 1 deletion(-) 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 index 5fb01ad514d..434badfbbc3 100644 --- a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap +++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/MessageDetailHeader.test.tsx.snap @@ -93,7 +93,6 @@ exports[`MessageDetailHeader component should match the snapshot with all props } /> Date: Wed, 31 Jan 2024 18:07:16 +0100 Subject: [PATCH 15/17] update snap --- .../__test__/__snapshots__/MessageDetailsScreen.test.tsx.snap | 1 - 1 file changed, 1 deletion(-) diff --git a/ts/features/pn/screens/__test__/__snapshots__/MessageDetailsScreen.test.tsx.snap b/ts/features/pn/screens/__test__/__snapshots__/MessageDetailsScreen.test.tsx.snap index d8f6acddb0a..4323d0c8345 100644 --- a/ts/features/pn/screens/__test__/__snapshots__/MessageDetailsScreen.test.tsx.snap +++ b/ts/features/pn/screens/__test__/__snapshots__/MessageDetailsScreen.test.tsx.snap @@ -783,7 +783,6 @@ exports[`MessageDetailsScreen should match the snapshot when everything went fin } /> Date: Fri, 2 Feb 2024 12:58:09 +0100 Subject: [PATCH 16/17] use logosForService --- .../MessageDetail/MessageDetailHeader.tsx | 5 +++-- ts/utils/services.ts | 13 ------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/ts/features/messages/components/MessageDetail/MessageDetailHeader.tsx b/ts/features/messages/components/MessageDetail/MessageDetailHeader.tsx index f541fcc9f37..ae3acb5fb8a 100644 --- a/ts/features/messages/components/MessageDetail/MessageDetailHeader.tsx +++ b/ts/features/messages/components/MessageDetail/MessageDetailHeader.tsx @@ -9,7 +9,7 @@ import { import { localeDateFormat } from "../../../../utils/locale"; import I18n from "../../../../i18n"; import { ServicePublic } from "../../../../../definitions/backend/ServicePublic"; -import { logoForService } from "../../../../utils/services"; +import { logosForService } from "../../../../utils/services"; import { OrganizationHeader } from "./OrganizationHeader"; export type MessageDetailHeaderProps = PropsWithChildren<{ @@ -47,8 +47,9 @@ export const MessageDetailHeader = ({ {service && ( <> + {/* TODO: update logoUri when MultiImage component will be available in DS */} diff --git a/ts/utils/services.ts b/ts/utils/services.ts index 2bf7c4a55c2..3dafbf24e50 100644 --- a/ts/utils/services.ts +++ b/ts/utils/services.ts @@ -9,19 +9,6 @@ import { contentRepoUrl } from "../config"; import { VisibleServicesState } from "../store/reducers/entities/services/visibleServices"; import { isTextIncludedCaseInsensitive } from "./strings"; -/** - * Returns the uri for the logo of a given service - * @param service - * @param logosRepoUrl - * @returns - */ -export const logoForService = ( - service: ServicePublic, - logosRepoUrl: string = `${contentRepoUrl}/logos` -): ImageURISource => ({ - uri: `${logosRepoUrl}/services/${service.service_id.toLowerCase()}.png` -}); - /** * Returns an array of ImageURISource pointing to possible logos for the * provided service. From b725c7f1df9d04c4cfecd5f8fbde5e682fde7bd7 Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Fri, 2 Feb 2024 14:21:43 +0100 Subject: [PATCH 17/17] update test --- img/test/logo.png | Bin 0 -> 5347 bytes .../__tests__/OrganizationHeader.test.tsx | 8 +++----- .../OrganizationHeader.test.tsx.snap | 6 +++--- 3 files changed, 6 insertions(+), 8 deletions(-) create mode 100644 img/test/logo.png diff --git a/img/test/logo.png b/img/test/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..fc44b0a3796c0e0a64c3d858ca038bd4570465d9 GIT binary patch literal 5347 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 X@eDJUQo;Ye2mwlRs { it("should match the snapshot", () => { const component = render( ); expect(component.toJSON()).toMatchSnapshot(); diff --git a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/OrganizationHeader.test.tsx.snap b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/OrganizationHeader.test.tsx.snap index 64937ad016d..8ccc1da6e7c 100644 --- a/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/OrganizationHeader.test.tsx.snap +++ b/ts/features/messages/components/MessageDetail/__tests__/__snapshots__/OrganizationHeader.test.tsx.snap @@ -47,7 +47,7 @@ exports[`OrganizationHeader component should match the snapshot 1`] = ` } weight="Bold" > - Universala Esperanto-Asocio + #### organization_name #### - Avviso + #### service name ####