From bdbd550a1a772f8c767f669aee3f0b720aa80aaf Mon Sep 17 00:00:00 2001 From: Alessandro Dell'Oste Date: Tue, 12 Nov 2024 17:15:25 +0100 Subject: [PATCH 01/35] replace Markdown with IOMarkdown --- ts/components/IOMarkdown/customRules.tsx | 56 +++++++++++++++++++ .../messages/screens/MessageDetailsScreen.tsx | 20 +++---- .../components/CardWithMarkdownContent.tsx | 27 +++++---- 3 files changed, 79 insertions(+), 24 deletions(-) create mode 100644 ts/components/IOMarkdown/customRules.tsx diff --git a/ts/components/IOMarkdown/customRules.tsx b/ts/components/IOMarkdown/customRules.tsx new file mode 100644 index 00000000000..47e1e27d496 --- /dev/null +++ b/ts/components/IOMarkdown/customRules.tsx @@ -0,0 +1,56 @@ +import { TxtHeaderNode, TxtLinkNode } from "@textlint/ast-node-types"; +import { + Body, + IOToast, + Label, + MdH1, + MdH2, + MdH3 +} from "@pagopa/io-app-design-system"; +import React from "react"; +import { isIoInternalLink } from "../ui/Markdown/handlers/link"; +import { handleInternalLink } from "../../utils/internalLink"; +import { openWebUrl } from "../../utils/url"; +import I18n from "../../i18n"; +import { getTxtNodeKey } from "./renderRules"; +import { IOMarkdownRenderRules, Renderer } from "./types"; + +const HEADINGS_MAP = { + 1: MdH1, + 2: MdH2, + 3: MdH3, + 4: Body, + 5: Body, + 6: Body +}; + +export const generateMessagesAndServicesRules = ( + linkTo: (path: string) => void +): Partial => ({ + Header(header: TxtHeaderNode, render: Renderer) { + const Heading = HEADINGS_MAP[header.depth]; + + return ( + + {header.children.map(render)} + + ); + }, + Link(link: TxtLinkNode, render: Renderer) { + const handlePress = () => { + if (isIoInternalLink(link.url)) { + handleInternalLink(linkTo, link.url); + } else { + openWebUrl(link.url, () => { + IOToast.error(I18n.t("global.jserror.title")); + }); + } + }; + + return ( + + ); + } +}); diff --git a/ts/features/messages/screens/MessageDetailsScreen.tsx b/ts/features/messages/screens/MessageDetailsScreen.tsx index bf85081bff9..03890bb97fb 100644 --- a/ts/features/messages/screens/MessageDetailsScreen.tsx +++ b/ts/features/messages/screens/MessageDetailsScreen.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useMemo, useRef } from "react"; import { ScrollView, StyleSheet, View } from "react-native"; import { ContentWrapper, Icon, VSpacer } from "@pagopa/io-app-design-system"; -import { useFocusEffect } from "@react-navigation/native"; +import { useFocusEffect, useLinkTo } from "@react-navigation/native"; import { pipe } from "fp-ts/lib/function"; import * as O from "fp-ts/lib/Option"; import * as pot from "@pagopa/ts-commons/lib/pot"; @@ -34,7 +34,6 @@ import { MessageDetailsHeader } from "../components/MessageDetail/MessageDetails import I18n from "../../../i18n"; import { messageDetailsByIdSelector } from "../store/reducers/detailsById"; import { MessageDetailsTagBox } from "../components/MessageDetail/MessageDetailsTagBox"; -import { MessageMarkdown } from "../components/MessageDetail/MessageMarkdown"; import { cleanMarkdownFromCTAs, getMessageCTA } from "../utils/messages"; import { MessageDetailsReminder } from "../components/MessageDetail/MessageDetailsReminder"; import { MessageDetailsFooter } from "../components/MessageDetail/MessageDetailsFooter"; @@ -49,7 +48,8 @@ import { trackPNOptInMessageOpened } from "../../pn/analytics"; import { RemoteContentBanner } from "../components/MessageDetail/RemoteContentBanner"; -import { setAccessibilityFocus } from "../../../utils/accessibility"; +import IOMarkdown from "../../../components/IOMarkdown"; +import { generateMessagesAndServicesRules } from "../../../components/IOMarkdown/customRules"; const styles = StyleSheet.create({ scrollContentContainer: { @@ -73,6 +73,7 @@ type MessageDetailsScreenProps = IOStackNavigationRouteProps< export const MessageDetailsScreen = (props: MessageDetailsScreenProps) => { const { messageId, serviceId } = props.route.params; + const linkTo = useLinkTo(); const navigation = useIONavigation(); const dispatch = useIODispatch(); const scrollViewRef = useRef(null); @@ -210,15 +211,10 @@ export const MessageDetailsScreen = (props: MessageDetailsScreenProps) => { messageId={messageId} title={subject} /> - { - setTimeout(() => { - setAccessibilityFocus(scrollViewRef); - }, 100); - }} - > - {markdownWithNoCTA} - + ( - - {content} - - ) + ({ content }: CardWithMarkdownContentProps) => { + const linkTo = useLinkTo(); + + return ( + + + + ); + } ); const CardWithMarkdownContentSkeleton = () => ( From 5d8dbdde7bb38c41c951c07f8c69aca0cc8b39ee Mon Sep 17 00:00:00 2001 From: Andrea Piai Date: Tue, 12 Nov 2024 18:32:27 +0100 Subject: [PATCH 02/35] IOMarkdown for message's preconditions --- .../components/Home/PreconditionsContent.tsx | 53 ++------------ .../__tests__/PreconditionsContent.test.tsx | 69 ------------------- .../PreconditionsContent.test.tsx.snap | 27 ++++++-- .../saga/handleMessagePrecondition.ts | 10 +-- .../actions/__tests__/preconditions.test.ts | 37 +++------- ts/features/messages/store/actions/index.ts | 2 - .../messages/store/actions/preconditions.ts | 19 ++--- .../__tests__/messagePrecondition.test.ts | 60 ++-------------- .../store/reducers/messagePrecondition.ts | 65 ++--------------- 9 files changed, 54 insertions(+), 288 deletions(-) diff --git a/ts/features/messages/components/Home/PreconditionsContent.tsx b/ts/features/messages/components/Home/PreconditionsContent.tsx index 1e78e78e52c..978f77e0289 100644 --- a/ts/features/messages/components/Home/PreconditionsContent.tsx +++ b/ts/features/messages/components/Home/PreconditionsContent.tsx @@ -1,27 +1,15 @@ -import React, { useCallback } from "react"; +import React from "react"; import { View } from "react-native"; import Placeholder from "rn-placeholder"; import { VSpacer } from "@pagopa/io-app-design-system"; +import { useIOSelector } from "../../../../store/hooks"; import { - useIODispatch, - useIOSelector, - useIOStore -} from "../../../../store/hooks"; -import { - preconditionsCategoryTagSelector, preconditionsContentMarkdownSelector, preconditionsContentSelector } from "../../store/reducers/messagePrecondition"; import I18n from "../../../../i18n"; import { pnMinAppVersionSelector } from "../../../../store/reducers/backendStatus/remoteConfig"; -import { MessageMarkdown } from "../MessageDetail/MessageMarkdown"; -import { - errorPreconditionStatusAction, - shownPreconditionStatusAction, - toErrorPayload, - toShownPayload -} from "../../store/actions/preconditions"; -import { trackDisclaimerLoadError } from "../../analytics"; +import IOMarkdown from "../../../../components/IOMarkdown"; import { PreconditionsFeedback } from "./PreconditionsFeedback"; export const PreconditionsContent = () => { @@ -40,44 +28,11 @@ export const PreconditionsContent = () => { }; const PreconditionsContentMarkdown = () => { - const dispatch = useIODispatch(); - const store = useIOStore(); - const markdown = useIOSelector(preconditionsContentMarkdownSelector); - - const onLoadEndCallback = useCallback(() => { - dispatch(shownPreconditionStatusAction(toShownPayload())); - }, [dispatch]); - const onErrorCallback = useCallback( - (anyError: any) => { - const state = store.getState(); - const category = preconditionsCategoryTagSelector(state); - if (category) { - trackDisclaimerLoadError(category); - } - dispatch( - errorPreconditionStatusAction( - toErrorPayload(`Markdown loading failure (${anyError})`) - ) - ); - }, - [dispatch, store] - ); - if (!markdown) { return null; } - - return ( - - {markdown} - - ); + return ; }; const PreconditionsContentError = () => ( diff --git a/ts/features/messages/components/Home/__tests__/PreconditionsContent.test.tsx b/ts/features/messages/components/Home/__tests__/PreconditionsContent.test.tsx index f71d0f86fb6..b05a3976091 100644 --- a/ts/features/messages/components/Home/__tests__/PreconditionsContent.test.tsx +++ b/ts/features/messages/components/Home/__tests__/PreconditionsContent.test.tsx @@ -9,15 +9,6 @@ import { PreconditionsContent } from "../PreconditionsContent"; import { MESSAGES_ROUTES } from "../../../navigation/routes"; import * as messagePreconditions from "../../../store/reducers/messagePrecondition"; import * as backendStatus from "../../../../../store/reducers/backendStatus/remoteConfig"; -import { MarkdownProps } from "../../../../../components/ui/Markdown/Markdown"; -import { - errorPreconditionStatusAction, - shownPreconditionStatusAction, - toErrorPayload, - toShownPayload -} from "../../../store/actions/preconditions"; -import { TagEnum } from "../../../../../../definitions/backend/MessageCategoryBase"; -import * as analytics from "../../../analytics"; import { mockAccessibilityInfo } from "../../../../../utils/testAccessibility"; jest.mock("../../MessageDetail/MessageMarkdown"); @@ -85,66 +76,6 @@ describe("PreconditionsContent", () => { const component = renderComponent(); expect(component.toJSON()).toMatchSnapshot(); }); - it("should dispatch `shownPreconditionStatusAction` when markdown loading completes", () => { - jest - .spyOn(messagePreconditions, "preconditionsContentSelector") - .mockImplementation(_ => "content"); - jest - .spyOn(messagePreconditions, "preconditionsContentMarkdownSelector") - .mockImplementation(_ => "A markdown content"); - const component = renderComponent(); - const mockMessageMarkdown = component.getByTestId( - "preconditions_content_message_markdown" - ); - const props = mockMessageMarkdown.props as Omit; - const onLoadEndCallback = props.onLoadEnd; - expect(onLoadEndCallback).toBeTruthy(); - - onLoadEndCallback?.(); - - expect(mockDispatch.mock.calls.length).toBe(1); - expect(mockDispatch.mock.calls[0][0]).toStrictEqual( - shownPreconditionStatusAction(toShownPayload()) - ); - }); - it("should track an error and dispatch 'errorPreconditionStatusAction' when an error occours during markdown loading", () => { - jest - .spyOn(messagePreconditions, "preconditionsContentSelector") - .mockImplementation(_ => "content"); - jest - .spyOn(messagePreconditions, "preconditionsContentMarkdownSelector") - .mockImplementation(_ => "A markdown content"); - const categoryTag = TagEnum.GENERIC; - jest - .spyOn(messagePreconditions, "preconditionsCategoryTagSelector") - .mockImplementation(_ => categoryTag); - const mockTrackDislaimerLoadError = jest.fn(); - jest - .spyOn(analytics, "trackDisclaimerLoadError") - .mockImplementation(mockTrackDislaimerLoadError); - const component = renderComponent(); - const mockMessageMarkdown = component.getByTestId( - "preconditions_content_message_markdown" - ); - const props = mockMessageMarkdown.props as Omit; - const onErrorCallback = props.onError; - expect(onErrorCallback).toBeTruthy(); - - const expectedError = new Error("An error"); - onErrorCallback?.(expectedError); - - expect(mockTrackDislaimerLoadError.mock.calls.length).toBe(1); - expect(mockTrackDislaimerLoadError.mock.calls[0][0]).toStrictEqual( - categoryTag - ); - - expect(mockDispatch.mock.calls.length).toBe(1); - expect(mockDispatch.mock.calls[0][0]).toStrictEqual( - errorPreconditionStatusAction( - toErrorPayload(`Markdown loading failure (${expectedError})`) - ) - ); - }); }); const renderComponent = () => { diff --git a/ts/features/messages/components/Home/__tests__/__snapshots__/PreconditionsContent.test.tsx.snap b/ts/features/messages/components/Home/__tests__/__snapshots__/PreconditionsContent.test.tsx.snap index 93d12fac8a4..272a9b66648 100644 --- a/ts/features/messages/components/Home/__tests__/__snapshots__/PreconditionsContent.test.tsx.snap +++ b/ts/features/messages/components/Home/__tests__/__snapshots__/PreconditionsContent.test.tsx.snap @@ -331,12 +331,27 @@ exports[`PreconditionsContent should match snapshot when content category is 'co } } > - - Mock Message Markdown + + + A markdown content + diff --git a/ts/features/messages/saga/handleMessagePrecondition.ts b/ts/features/messages/saga/handleMessagePrecondition.ts index 407ebd61c8e..4bdd85c5e9d 100644 --- a/ts/features/messages/saga/handleMessagePrecondition.ts +++ b/ts/features/messages/saga/handleMessagePrecondition.ts @@ -11,10 +11,10 @@ import { trackDisclaimerLoadError } from "../analytics"; import { errorPreconditionStatusAction, idlePreconditionStatusAction, - loadingContentPreconditionStatusAction, retrievingDataPreconditionStatusAction, + shownPreconditionStatusAction, toErrorPayload, - toLoadingContentPayload + toShownPayload } from "../store/actions/preconditions"; import { UIMessageId } from "../types"; import { MessageCategory } from "../../../../definitions/backend/MessageCategory"; @@ -62,11 +62,7 @@ function* messagePreconditionWorker( if (E.isRight(result)) { if (result.right.status === 200) { const content = result.right.value; - yield* put( - loadingContentPreconditionStatusAction( - toLoadingContentPayload(content) - ) - ); + yield* put(shownPreconditionStatusAction(toShownPayload(content))); return; } throw Error(`response status ${result.right.status}`); diff --git a/ts/features/messages/store/actions/__tests__/preconditions.test.ts b/ts/features/messages/store/actions/__tests__/preconditions.test.ts index 38603164830..8e11f71abfd 100644 --- a/ts/features/messages/store/actions/__tests__/preconditions.test.ts +++ b/ts/features/messages/store/actions/__tests__/preconditions.test.ts @@ -4,13 +4,11 @@ import { UIMessageId } from "../../../types"; import { errorPreconditionStatusAction, idlePreconditionStatusAction, - loadingContentPreconditionStatusAction, retrievingDataPreconditionStatusAction, scheduledPreconditionStatusAction, shownPreconditionStatusAction, toErrorPayload, toIdlePayload, - toLoadingContentPayload, toRetrievingDataPayload, toScheduledPayload, toShownPayload, @@ -29,15 +27,6 @@ describe("Action payload generators", () => { const idlePayload = toIdlePayload(); expect(idlePayload.nextStatus).toStrictEqual("idle"); }); - it("should generate proper payload with 'toLoadingContentPayload'", () => { - const content: ThirdPartyMessagePrecondition = { - title: "The title", - markdown: "The content" - }; - const loadingContentPayload = toLoadingContentPayload(content); - expect(loadingContentPayload.nextStatus).toStrictEqual("loadingContent"); - expect(loadingContentPayload.content).toStrictEqual(content); - }); it("should generate proper payload with 'toRetrievingDataPayload'", () => { const retrievingDataPayload = toRetrievingDataPayload(); expect(retrievingDataPayload.nextStatus).toStrictEqual("retrievingData"); @@ -51,8 +40,13 @@ describe("Action payload generators", () => { expect(scheduledPayload.categoryTag).toStrictEqual(categoryTag); }); it("should generate proper payload with 'toShownPayload'", () => { - const shownPayload = toShownPayload(); + const content: ThirdPartyMessagePrecondition = { + title: "A title", + markdown: "A markdown" + }; + const shownPayload = toShownPayload(content); expect(shownPayload.nextStatus).toStrictEqual("shown"); + expect(shownPayload.content).toBe(content); }); it("should generate proper payload with 'toUpdateRequiredPayload'", () => { const updateRequiredPayload = toUpdateRequiredPayload(); @@ -73,19 +67,6 @@ describe("Action generators", () => { expect(idlePSA.type).toStrictEqual("TO_IDLE_PRECONDITION_STATUS"); expect(idlePSA.payload).toStrictEqual(idlePayload); }); - it("should return the proper action data for 'loadingContentPreconditionStatusAction'", () => { - const loadingContentPayload = toLoadingContentPayload({ - title: "", - markdown: "" - }); - const loadingContentPSA = loadingContentPreconditionStatusAction( - loadingContentPayload - ); - expect(loadingContentPSA.type).toStrictEqual( - "TO_LOADING_CONTENT_PRECONDITION_STATUS" - ); - expect(loadingContentPSA.payload).toStrictEqual(loadingContentPayload); - }); it("should return the proper action data for 'retrievingDataPreconditionStatusAction'", () => { const retrievingDatPayload = toRetrievingDataPayload(); const rerievingDataPSA = @@ -105,7 +86,11 @@ describe("Action generators", () => { expect(scheduledPSA.payload).toStrictEqual(scheduledPayload); }); it("should return the proper action data for 'shownPreconditionStatusAction'", () => { - const shownPayload = toShownPayload(); + const content: ThirdPartyMessagePrecondition = { + title: "A title", + markdown: "A markdown" + }; + const shownPayload = toShownPayload(content); const shownPSA = shownPreconditionStatusAction(shownPayload); expect(shownPSA.type).toStrictEqual("TO_SHOWN_PRECONDITION_STATUS"); expect(shownPSA.payload).toStrictEqual(shownPayload); diff --git a/ts/features/messages/store/actions/index.ts b/ts/features/messages/store/actions/index.ts index 5ad1adf4219..d0eff1bd262 100644 --- a/ts/features/messages/store/actions/index.ts +++ b/ts/features/messages/store/actions/index.ts @@ -15,7 +15,6 @@ import { MessageListCategory } from "../../types/messageListCategory"; import { errorPreconditionStatusAction, idlePreconditionStatusAction, - loadingContentPreconditionStatusAction, retrievingDataPreconditionStatusAction, scheduledPreconditionStatusAction, shownPreconditionStatusAction, @@ -317,7 +316,6 @@ export type MessagesActions = ActionType< | typeof removeCachedAttachment | typeof errorPreconditionStatusAction | typeof idlePreconditionStatusAction - | typeof loadingContentPreconditionStatusAction | typeof retrievingDataPreconditionStatusAction | typeof scheduledPreconditionStatusAction | typeof shownPreconditionStatusAction diff --git a/ts/features/messages/store/actions/preconditions.ts b/ts/features/messages/store/actions/preconditions.ts index 1c691b42f07..f69467fd129 100644 --- a/ts/features/messages/store/actions/preconditions.ts +++ b/ts/features/messages/store/actions/preconditions.ts @@ -11,10 +11,6 @@ export type NPSError = { export type NPSIdle = { nextStatus: "idle"; }; -export type NPSLoadingContent = { - nextStatus: "loadingContent"; - content: ThirdPartyMessagePrecondition; -}; export type NPSRetrievingData = { nextStatus: "retrievingData"; }; @@ -24,6 +20,7 @@ export type NPSScheduled = { categoryTag: MessageCategory["tag"]; }; export type NPSShown = { + content: ThirdPartyMessagePrecondition; nextStatus: "shown"; }; export type NPSUpdateRequired = { @@ -37,12 +34,6 @@ export const toErrorPayload = (reason: string): NPSError => ({ export const toIdlePayload = (): NPSIdle => ({ nextStatus: "idle" }); -export const toLoadingContentPayload = ( - content: ThirdPartyMessagePrecondition -): NPSLoadingContent => ({ - nextStatus: "loadingContent", - content -}); export const toRetrievingDataPayload = (): NPSRetrievingData => ({ nextStatus: "retrievingData" }); @@ -54,7 +45,10 @@ export const toScheduledPayload = ( messageId, categoryTag }); -export const toShownPayload = (): NPSShown => ({ +export const toShownPayload = ( + content: ThirdPartyMessagePrecondition +): NPSShown => ({ + content, nextStatus: "shown" }); export const toUpdateRequiredPayload = (): NPSUpdateRequired => ({ @@ -67,9 +61,6 @@ export const errorPreconditionStatusAction = createStandardAction( export const idlePreconditionStatusAction = createStandardAction( "TO_IDLE_PRECONDITION_STATUS" )(); -export const loadingContentPreconditionStatusAction = createStandardAction( - "TO_LOADING_CONTENT_PRECONDITION_STATUS" -)(); export const retrievingDataPreconditionStatusAction = createStandardAction( "TO_RETRIEVING_DATA_PRECONDITION_STATUS" )(); diff --git a/ts/features/messages/store/reducers/__tests__/messagePrecondition.test.ts b/ts/features/messages/store/reducers/__tests__/messagePrecondition.test.ts index 0ee3acbefe0..68ee89896db 100644 --- a/ts/features/messages/store/reducers/__tests__/messagePrecondition.test.ts +++ b/ts/features/messages/store/reducers/__tests__/messagePrecondition.test.ts @@ -7,13 +7,11 @@ import { UIMessageId } from "../../../types"; import { errorPreconditionStatusAction, idlePreconditionStatusAction, - loadingContentPreconditionStatusAction, retrievingDataPreconditionStatusAction, scheduledPreconditionStatusAction, shownPreconditionStatusAction, toErrorPayload, toIdlePayload, - toLoadingContentPayload, toRetrievingDataPayload, toScheduledPayload, toShownPayload, @@ -35,7 +33,6 @@ import { shouldPresentPreconditionsBottomSheetSelector, toErrorMPS, toIdleMPS, - toLoadingContentMPS, toRetrievingDataMPS, toScheduledMPS, toShownMPS, @@ -57,7 +54,6 @@ const messagePreconditionStatusesGenerator = ( ) => [ toErrorMPS(messageId, inputCategoryTag, errorReason), toIdleMPS(), - toLoadingContentMPS(messageId, inputCategoryTag, content), toRetrievingDataMPS(messageId, inputCategoryTag), toScheduledMPS(messageId, inputCategoryTag), toShownMPS(messageId, inputCategoryTag, content), @@ -69,7 +65,6 @@ const computeExpectedOutput = ( withAction: ActionType< | typeof errorPreconditionStatusAction | typeof idlePreconditionStatusAction - | typeof loadingContentPreconditionStatusAction | typeof retrievingDataPreconditionStatusAction | typeof scheduledPreconditionStatusAction | typeof shownPreconditionStatusAction @@ -97,7 +92,7 @@ const computeExpectedOutput = ( ); } break; - case "loadingContent": + case "retrievingData": switch (withAction.type) { case "TO_IDLE_PRECONDITION_STATUS": return toIdleMPS(); @@ -109,24 +104,6 @@ const computeExpectedOutput = ( ); case "TO_SHOWN_PRECONDITION_STATUS": return toShownMPS( - fromStatus.messageId, - fromStatus.categoryTag, - fromStatus.content - ); - } - break; - case "retrievingData": - switch (withAction.type) { - case "TO_IDLE_PRECONDITION_STATUS": - return toIdleMPS(); - case "TO_ERROR_PRECONDITION_STATUS": - return toErrorMPS( - fromStatus.messageId, - fromStatus.categoryTag, - withAction.payload.reason - ); - case "TO_LOADING_CONTENT_PRECONDITION_STATUS": - return toLoadingContentMPS( fromStatus.messageId, fromStatus.categoryTag, withAction.payload.content @@ -159,12 +136,11 @@ describe("messagePrecondition reducer", () => { const changeStatusActions = [ errorPreconditionStatusAction(toErrorPayload(errorReason)), idlePreconditionStatusAction(toIdlePayload()), - loadingContentPreconditionStatusAction(toLoadingContentPayload(content)), retrievingDataPreconditionStatusAction(toRetrievingDataPayload()), scheduledPreconditionStatusAction( toScheduledPayload(messageId, categoryTag) ), - shownPreconditionStatusAction(toShownPayload()), + shownPreconditionStatusAction(toShownPayload(content)), updateRequiredPreconditionStatusAction(toUpdateRequiredPayload()) ]; messagePreconditionStatusesGenerator(TagEnum.GENERIC).forEach(initialStatus => @@ -206,23 +182,6 @@ describe("Message precondition status generators", () => { const mps = toIdleMPS(); expect(mps).toStrictEqual(expectedMPS); }); - it("should return proper istance for 'toLoadingContentMPS'", () => { - const expectedMPS = { - state: "loadingContent", - messageId, - categoryTag, - content: { - title: "A title", - markdown: "A markdown content" - } - }; - const mps = toLoadingContentMPS( - expectedMPS.messageId, - expectedMPS.categoryTag, - expectedMPS.content - ); - expect(mps).toStrictEqual(expectedMPS); - }); it("should return proper istance for 'toRetrievingDataMPS'", () => { const expectedMPS = { state: "retrievingData", @@ -274,7 +233,6 @@ describe("foldPreconditionStatus", () => { jest.fn(), jest.fn(), jest.fn(), - jest.fn(), jest.fn() ]; @@ -291,8 +249,7 @@ describe("foldPreconditionStatus", () => { mocks[2], mocks[3], mocks[4], - mocks[5], - mocks[6] + mocks[5] )(status); mocks.forEach((mock, mockIndex) => { if (statusIndex === mockIndex) { @@ -373,7 +330,6 @@ describe("preconditionsTitleContentSelector", () => { const expectedOutput = [ "empty", undefined, - "header", "loading", undefined, "header", @@ -398,9 +354,7 @@ describe("preconditionsTitleContentSelector", () => { describe("preconditionsTitleSelector", () => { messagePreconditionStatusesGenerator(TagEnum.GENERIC).forEach(status => { const expectedOutput = - status.state === "loadingContent" || status.state === "shown" - ? status.content.title - : undefined; + status.state === "shown" ? status.content.title : undefined; it(`should return '${expectedOutput}' for status '${status.state}'`, () => { const globalStatus = { entities: { @@ -419,7 +373,6 @@ describe("preconditionsContentSelector", () => { const expectedOutput = [ "error", undefined, - "content", "loading", undefined, "content", @@ -444,9 +397,7 @@ describe("preconditionsContentSelector", () => { describe("preconditionsContentMarkdownSelector", () => { messagePreconditionStatusesGenerator(TagEnum.GENERIC).forEach(status => { const expectedOutput = - status.state === "loadingContent" || status.state === "shown" - ? status.content.markdown - : undefined; + status.state === "shown" ? status.content.markdown : undefined; it(`should return '${expectedOutput}' for status '${status.state}'`, () => { const globalStatus = { entities: { @@ -467,7 +418,6 @@ describe("preconditionsFooterSelector", () => { "view", undefined, "view", - "view", undefined, "content", "update" diff --git a/ts/features/messages/store/reducers/messagePrecondition.ts b/ts/features/messages/store/reducers/messagePrecondition.ts index 8abf4a4aaa8..84d0fd5b695 100644 --- a/ts/features/messages/store/reducers/messagePrecondition.ts +++ b/ts/features/messages/store/reducers/messagePrecondition.ts @@ -12,7 +12,6 @@ import { UIMessageId } from "../../types"; import { errorPreconditionStatusAction, idlePreconditionStatusAction, - loadingContentPreconditionStatusAction, retrievingDataPreconditionStatusAction, scheduledPreconditionStatusAction, shownPreconditionStatusAction, @@ -33,12 +32,6 @@ type MPSError = { type MPSIdle = { state: "idle"; }; -type MPSLoadingContent = { - state: "loadingContent"; - messageId: UIMessageId; - categoryTag: MessageCategory["tag"]; - content: ThirdPartyMessagePrecondition; -}; type MPSRetrievingData = { state: "retrievingData"; messageId: UIMessageId; @@ -62,7 +55,6 @@ type MPSUpdateRequired = { export type MessagePreconditionStatus = | MPSError | MPSIdle - | MPSLoadingContent | MPSRetrievingData | MPSScheduled | MPSShown @@ -81,12 +73,6 @@ export const preconditionReducer = ( return foldPreconditionStatus( () => state, () => state, - loadingContentStatus => - toErrorMPS( - loadingContentStatus.messageId, - loadingContentStatus.categoryTag, - action.payload.reason - ), // From Loading Content to Error retrievingDataStatus => toErrorMPS( retrievingDataStatus.messageId, @@ -101,34 +87,17 @@ export const preconditionReducer = ( return foldPreconditionStatus( () => toIdleMPS(), // From Error to Idle () => state, - () => toIdleMPS(), // From Loading Content to Idle, () => toIdleMPS(), // From Retrieving Data to Idle, () => state, () => toIdleMPS(), // From Shown to Idle () => toIdleMPS() // From Update Required to Idle )(state); - case getType(loadingContentPreconditionStatusAction): - return foldPreconditionStatus( - () => state, - () => state, - () => state, - retrievingDataStatus => - toLoadingContentMPS( - retrievingDataStatus.messageId, - retrievingDataStatus.categoryTag, - action.payload.content - ), // From Retrieving Data to Loading Content - () => state, - () => state, - () => state - )(state); case getType(retrievingDataPreconditionStatusAction): return foldPreconditionStatus( errorStatus => toRetrievingDataMPS(errorStatus.messageId, errorStatus.categoryTag), // From Error to Retrieving Data () => state, () => state, - () => state, scheduledStatus => toRetrievingDataMPS( scheduledStatus.messageId, @@ -145,20 +114,18 @@ export const preconditionReducer = ( () => state, () => state, () => state, - () => state, () => state )(state); case getType(shownPreconditionStatusAction): return foldPreconditionStatus( () => state, () => state, - loadingContentStatus => + retrievingDataStatus => toShownMPS( - loadingContentStatus.messageId, - loadingContentStatus.categoryTag, - loadingContentStatus.content - ), // From Loading Content to Shown - () => state, + retrievingDataStatus.messageId, + retrievingDataStatus.categoryTag, + action.payload.content + ), // From Retrieving Data to Shown () => state, () => state, () => state @@ -168,7 +135,6 @@ export const preconditionReducer = ( () => state, () => state, () => state, - () => state, () => toUpdateRequiredMPS(), // From Scheduled to Update Required, () => state, () => state @@ -190,16 +156,6 @@ export const toErrorMPS = ( export const toIdleMPS = (): MPSIdle => ({ state: "idle" }); -export const toLoadingContentMPS = ( - messageId: UIMessageId, - categoryTag: MessageCategory["tag"], - content: ThirdPartyMessagePrecondition -): MPSLoadingContent => ({ - state: "loadingContent", - messageId, - categoryTag, - content -}); export const toRetrievingDataMPS = ( messageId: UIMessageId, categoryTag: MessageCategory["tag"] @@ -234,7 +190,6 @@ export const foldPreconditionStatus = ( onError: (status: MPSError) => A, onIdle: (status: MPSIdle) => A, - onLoadingContent: (status: MPSLoadingContent) => A, onRetrievingData: (status: MPSRetrievingData) => A, onScheduled: (status: MPSScheduled) => A, onShown: (status: MPSShown) => A, @@ -244,8 +199,6 @@ export const foldPreconditionStatus = switch (status.state) { case "error": return onError(status); - case "loadingContent": - return onLoadingContent(status); case "retrievingData": return onRetrievingData(status); case "scheduled": @@ -269,7 +222,6 @@ export const preconditionsRequireAppUpdateSelector = (state: GlobalState) => constFalse, constFalse, constFalse, - constFalse, scheduled => pipe( scheduled.categoryTag === SENDTagEnum.PN, @@ -292,7 +244,6 @@ export const preconditionsTitleContentSelector = (state: GlobalState) => foldPreconditionStatus( () => "empty" as const, constUndefined, - () => "header" as const, () => "loading" as const, constUndefined, () => "header" as const, @@ -306,7 +257,6 @@ export const preconditionsTitleSelector = (state: GlobalState) => foldPreconditionStatus( constUndefined, constUndefined, - loadingContentStatus => loadingContentStatus.content.title, constUndefined, constUndefined, shownStatus => shownStatus.content.title, @@ -320,7 +270,6 @@ export const preconditionsContentSelector = (state: GlobalState) => foldPreconditionStatus( _ => "error" as const, constUndefined, - _ => "content" as const, _ => "loading" as const, constUndefined, _ => "content" as const, @@ -334,7 +283,6 @@ export const preconditionsContentMarkdownSelector = (state: GlobalState) => foldPreconditionStatus( constUndefined, constUndefined, - loadingContentStatus => loadingContentStatus.content.markdown, constUndefined, constUndefined, shownStatus => shownStatus.content.markdown, @@ -349,7 +297,6 @@ export const preconditionsFooterSelector = (state: GlobalState) => _ => "view" as const, constUndefined, _ => "view" as const, - _ => "view" as const, constUndefined, _ => "content" as const, _ => "update" as const @@ -362,7 +309,6 @@ export const preconditionsCategoryTagSelector = (state: GlobalState) => foldPreconditionStatus( errorStatus => errorStatus.categoryTag, constUndefined, - loadingContentStatus => loadingContentStatus.categoryTag, retrievingDataStatus => retrievingDataStatus.categoryTag, scheduledStatus => scheduledStatus.categoryTag, shownStatus => shownStatus.categoryTag, @@ -376,7 +322,6 @@ export const preconditionsMessageIdSelector = (state: GlobalState) => foldPreconditionStatus( errorStatus => errorStatus.messageId, constUndefined, - loadingContentStatus => loadingContentStatus.messageId, retrievingDataStatus => retrievingDataStatus.messageId, scheduledStatus => scheduledStatus.messageId, shownStatus => shownStatus.messageId, From a0aba8ba68f55443fbe80b5c9fb1cbb77adf8ddd Mon Sep 17 00:00:00 2001 From: Damiano Plebani Date: Fri, 22 Nov 2024 10:06:39 +0100 Subject: [PATCH 03/35] Remove legacy `Markdown` playground from Developer mode section --- ts/navigation/ProfileNavigator.tsx | 5 - ts/navigation/params/ProfileParamsList.ts | 1 - ts/navigation/routes.ts | 1 - ts/screens/profile/DeveloperModeSection.tsx | 7 - .../playgrounds/MarkdownPlayground.tsx | 220 ------------------ 5 files changed, 234 deletions(-) delete mode 100644 ts/screens/profile/playgrounds/MarkdownPlayground.tsx diff --git a/ts/navigation/ProfileNavigator.tsx b/ts/navigation/ProfileNavigator.tsx index 8b6762c1916..13a0faae6c9 100644 --- a/ts/navigation/ProfileNavigator.tsx +++ b/ts/navigation/ProfileNavigator.tsx @@ -26,7 +26,6 @@ import ShareDataScreen from "../screens/profile/ShareDataScreen"; import TosScreen from "../screens/profile/TosScreen"; import { IdPayCodePlayGround } from "../screens/profile/playgrounds/IdPayCodePlayground"; import IdPayOnboardingPlayground from "../screens/profile/playgrounds/IdPayOnboardingPlayground"; -import MarkdownPlayground from "../screens/profile/playgrounds/MarkdownPlayground"; import { isGestureEnabled } from "../utils/navigation"; import TrialSystemPlayground from "../screens/profile/TrialSystemPlayground"; import ProfileMainScreen from "../screens/profile/ProfileMainScreen"; @@ -127,10 +126,6 @@ const ProfileStackNavigator = () => { name={ROUTES.PROFILE_DOWNLOAD_DATA} component={DownloadProfileDataScreen} /> - { screen: ROUTES.LOLLIPOP_PLAYGROUND }) }, - { - value: "Markdown", - onPress: () => - navigation.navigate(ROUTES.PROFILE_NAVIGATOR, { - screen: ROUTES.MARKDOWN_PLAYGROUND - }) - }, { value: "IO Markdown", onPress: () => diff --git a/ts/screens/profile/playgrounds/MarkdownPlayground.tsx b/ts/screens/profile/playgrounds/MarkdownPlayground.tsx deleted file mode 100644 index 335dea36d93..00000000000 --- a/ts/screens/profile/playgrounds/MarkdownPlayground.tsx +++ /dev/null @@ -1,220 +0,0 @@ -import { - ButtonOutline, - ButtonSolid, - HSpacer, - IOColors, - IOVisualCostants, - IconButtonSolid, - LabelSmall, - VSpacer -} from "@pagopa/io-app-design-system"; -import { useLinkTo } from "@react-navigation/native"; -import * as O from "fp-ts/lib/Option"; -import React, { useCallback } from "react"; -import { ScrollView, StyleSheet, TextInput, View } from "react-native"; -import I18n from "../../../i18n"; -import { MessageBodyMarkdown } from "../../../../definitions/backend/MessageBodyMarkdown"; -import LegacyMarkdown from "../../../components/ui/Markdown/LegacyMarkdown"; -import { CTA } from "../../../features/messages/types/MessageCTA"; -import { - cleanMarkdownFromCTAs, - getMessageCTA, - handleCtaAction -} from "../../../features/messages/utils/messages"; -import { useHeaderSecondLevel } from "../../../hooks/useHeaderSecondLevel"; -import { maybeNotNullyString } from "../../../utils/strings"; - -const styles = StyleSheet.create({ - textInput: { - flex: 1, - padding: 8, - borderWidth: 1, - borderRadius: 8, - borderColor: IOColors["grey-450"], - height: 64 - }, - row: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center" - }, - horizontalScroll: { - flexShrink: 1, - marginLeft: -IOVisualCostants.appMarginDefault, - marginRight: -IOVisualCostants.appMarginDefault, - paddingHorizontal: IOVisualCostants.appMarginDefault - } -}); - -const MARKDOWN_REFERENCE = I18n.t("global.markdown.reference"); - -const MARKDOWN_HEADING = `# I am a Header 1 -# I am a Header 1 with a looong looong looooong title - -## I am a Header 2 -## I am a Header 2 with a looong looong looooong title - -### I am a Header 3 -### I am a Header 3 with a looong looong looooong title - -#### I am a Header 4 -#### I am a Header 4 with a looong looong looooong title - -##### I am a Header 5 -##### I am a Header 5 with a looong looong looooong loooong title - -###### I am a Header 6 -###### I am a Header 6 with a looong looong looooong loooooong title -`; - -const MARKDOWN_PARAGRAPH = `A simple paragraph. - -Text can be emphasized with *asterisk* or _underscore_. - -If you need bold use **double asterisk**. -`; - -const MARKDOWN_LIST = `Unordered list: - -* React -* Vue -* Angular - -Ordered list: - -1. React -2. Vue -3. Angular -`; - -const MarkdownPlayground = () => { - const [markdownText, setMarkdownText] = React.useState(""); - const [inputText, setInputText] = React.useState(""); - - const setMarkdown = (markdownString: string) => { - setMarkdownText(markdownString); - setInputText(markdownString); - }; - - useHeaderSecondLevel({ - title: "Markdown playground" - }); - - const linkTo = useLinkTo(); - const handleCtaPress = useCallback( - (cta: CTA) => handleCtaAction(cta, linkTo), - [linkTo] - ); - - const maybeCTA = getMessageCTA(markdownText as MessageBodyMarkdown); - const ctaMessage = O.isSome(maybeCTA) - ? `${maybeCTA.value.cta_1 ? "2" : "1"} cta found!` - : "No CTA found"; - const isMarkdownSet = O.isSome(maybeNotNullyString(markdownText)); - - return ( - - - - - - - setMarkdownText(inputText)} - accessibilityLabel="Invia" - /> - - - - - - setMarkdown(MARKDOWN_HEADING)} - /> - - setMarkdown(MARKDOWN_PARAGRAPH)} - /> - - setMarkdown(MARKDOWN_LIST)} - /> - - setMarkdown(MARKDOWN_REFERENCE)} - /> - - - - - setMarkdown("")} - label="Clear" - accessibilityLabel="Clear" - /> - - - - {isMarkdownSet && {ctaMessage}} - - {O.isSome(maybeCTA) && ( - - handleCtaPress(maybeCTA.value.cta_1)} - /> - - )} - {O.isSome(maybeCTA) && maybeCTA.value.cta_2 && ( - <> - - - - maybeCTA.value.cta_2 - ? handleCtaPress(maybeCTA.value.cta_2) - : undefined - } - /> - - - )} - {isMarkdownSet && ( - <> - - - {cleanMarkdownFromCTAs(markdownText as MessageBodyMarkdown)} - - - )} - - - ); -}; - -export default MarkdownPlayground; From 3da8939ed5d691ff5356d6725e53019714ec9c1f Mon Sep 17 00:00:00 2001 From: Damiano Plebani Date: Mon, 25 Nov 2024 17:42:11 +0100 Subject: [PATCH 04/35] Remove `LegacyMarkdown` from `ManualConfigBottomSheet` --- .../services/ManualConfigBottomSheet.tsx | 45 +++++++------------ 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/ts/screens/profile/components/services/ManualConfigBottomSheet.tsx b/ts/screens/profile/components/services/ManualConfigBottomSheet.tsx index 7d6d8ffc0e5..a7f0df01c45 100644 --- a/ts/screens/profile/components/services/ManualConfigBottomSheet.tsx +++ b/ts/screens/profile/components/services/ManualConfigBottomSheet.tsx @@ -1,18 +1,15 @@ +import { FooterActionsInline } from "@pagopa/io-app-design-system"; import * as React from "react"; -import { VSpacer, FooterWithButtons } from "@pagopa/io-app-design-system"; -import LegacyMarkdown from "../../../../components/ui/Markdown/LegacyMarkdown"; +import IOMarkdown from "../../../../components/IOMarkdown"; import I18n from "../../../../i18n"; import { useIOBottomSheetAutoresizableModal } from "../../../../utils/hooks/bottomSheet"; -const SNAP_POINT_VALUE = 250; +const SNAP_POINT_VALUE = 200; const ManualConfigConfirm = (): React.ReactElement => ( - <> - - - {I18n.t("services.optIn.preferences.manualConfig.bottomSheet.body")} - - + ); export const useManualConfigBottomSheet = (onConfirm: () => void) => { @@ -28,26 +25,18 @@ export const useManualConfigBottomSheet = (onConfirm: () => void) => { component: , fullScreen: true, footer: ( - dismiss(), - accessibilityLabel: I18n.t("global.buttons.cancel") - } + dismiss() }} - secondary={{ - type: "Solid", - buttonProps: { - color: "danger", - label: I18n.t("global.buttons.confirm"), - accessibilityLabel: I18n.t("global.buttons.confirm"), - onPress: () => { - onConfirm(); - dismiss(); - } + endAction={{ + color: "danger", + label: I18n.t("global.buttons.confirm"), + onPress: () => { + onConfirm(); + dismiss(); } }} /> From 310b79854ce8e23dc7b24ba0f5288c3870907f13 Mon Sep 17 00:00:00 2001 From: Damiano Plebani Date: Mon, 25 Nov 2024 17:49:18 +0100 Subject: [PATCH 05/35] Remove `LegacyMarkdown` from `ShareDataFeatureInfos` --- .../ShareDataComponent/ShareDataFeatureInfos.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ts/screens/profile/components/ShareDataComponent/ShareDataFeatureInfos.tsx b/ts/screens/profile/components/ShareDataComponent/ShareDataFeatureInfos.tsx index 5209616e59a..a98156e21e3 100644 --- a/ts/screens/profile/components/ShareDataComponent/ShareDataFeatureInfos.tsx +++ b/ts/screens/profile/components/ShareDataComponent/ShareDataFeatureInfos.tsx @@ -8,7 +8,7 @@ import { Millisecond } from "@pagopa/ts-commons/lib/units"; import React, { useCallback, useMemo, useRef } from "react"; import { View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; -import LegacyMarkdown from "../../../../components/ui/Markdown/LegacyMarkdown"; +import IOMarkdown from "../../../../components/IOMarkdown"; import I18n from "../../../../i18n"; import { ioSuppliersUrl } from "../../../../urls"; import { setAccessibilityFocus } from "../../../../utils/accessibility"; @@ -43,10 +43,11 @@ const MarkdownBody = () => { accessibilityElementsHidden importantForAccessibility="no-hide-descendants" > - - {I18n.t("profile.main.privacy.shareData.whyBottomSheet.body")} - + + {bottom === 0 && } ); From be908542e834a9f82bb1acc311b7f92b7f35f0ef Mon Sep 17 00:00:00 2001 From: Damiano Plebani Date: Mon, 25 Nov 2024 17:59:26 +0100 Subject: [PATCH 06/35] Remove `LegacyMarkdown` from IDPay related screens --- .../idpay/details/components/InitiativeRulesInfoBox.tsx | 5 +++-- .../idpay/onboarding/screens/PDNDPrerequisitesScreen.tsx | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ts/features/idpay/details/components/InitiativeRulesInfoBox.tsx b/ts/features/idpay/details/components/InitiativeRulesInfoBox.tsx index 2b5eb2cba61..c8f2fa23740 100644 --- a/ts/features/idpay/details/components/InitiativeRulesInfoBox.tsx +++ b/ts/features/idpay/details/components/InitiativeRulesInfoBox.tsx @@ -13,7 +13,7 @@ import React from "react"; import { StyleSheet, View } from "react-native"; import Placeholder from "rn-placeholder"; import { IOStyles } from "../../../../components/core/variables/IOStyles"; -import LegacyMarkdown from "../../../../components/ui/Markdown/LegacyMarkdown"; +import IOMarkdown from "../../../../components/IOMarkdown"; import I18n from "../../../../i18n"; import { useIOBottomSheetAutoresizableModal } from "../../../../utils/hooks/bottomSheet"; @@ -26,9 +26,10 @@ const InitiativeRulesInfoBox = (props: Props) => { const { bottomSheet, present, dismiss } = useIOBottomSheetAutoresizableModal( { - component: {content}, + component: , title: I18n.t("idpay.initiative.beneficiaryDetails.infoModal.title"), footer: ( + // TODO: Replace this chunk of code using `FooterActions` { "idpay.onboarding.PDNDPrerequisites.prerequisites.info.header" ), component: ( - - {I18n.t( + + /> ), footer: ( + // TODO: Replace this chunk of code using `FooterActions` Date: Tue, 26 Nov 2024 10:44:35 +0100 Subject: [PATCH 07/35] RootedDeviceModal --- ts/screens/modal/RootedDeviceModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ts/screens/modal/RootedDeviceModal.tsx b/ts/screens/modal/RootedDeviceModal.tsx index d8254504903..c5e345b6137 100644 --- a/ts/screens/modal/RootedDeviceModal.tsx +++ b/ts/screens/modal/RootedDeviceModal.tsx @@ -13,7 +13,7 @@ import { ScrollView } from "react-native-gesture-handler"; import { SafeAreaView } from "react-native-safe-area-context"; import { useDispatch } from "react-redux"; import { IOStyles } from "../../components/core/variables/IOStyles"; -import LegacyMarkdown from "../../components/ui/Markdown/LegacyMarkdown"; +import IOMarkdown from "../../components/IOMarkdown"; import I18n from "../../i18n"; import { useIONavigation } from "../../navigation/params/AppParamsList"; import { continueWithRootOrJailbreak } from "../../store/actions/persistedPreferences"; @@ -41,7 +41,7 @@ const RootedDeviceModal = () => { bottomSheet: learnMoreBottomSheet } = useIOBottomSheetAutoresizableModal({ title: I18n.t("rooted.learnMoreBottomsheet.title"), - component: {body} + component: }); return ( From c3ba3f89ea23db9068679e9ed3a08443e15173e2 Mon Sep 17 00:00:00 2001 From: Andrea Piai Date: Tue, 26 Nov 2024 11:32:34 +0100 Subject: [PATCH 08/35] CiePinScreen --- ts/screens/authentication/cie/CiePinScreen.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ts/screens/authentication/cie/CiePinScreen.tsx b/ts/screens/authentication/cie/CiePinScreen.tsx index 48be76d1a7b..243a95b6dd5 100644 --- a/ts/screens/authentication/cie/CiePinScreen.tsx +++ b/ts/screens/authentication/cie/CiePinScreen.tsx @@ -40,7 +40,7 @@ import { BottomTopAnimation, LightModalContext } from "../../../components/ui/LightModal"; -import LegacyMarkdown from "../../../components/ui/Markdown/LegacyMarkdown"; +import IOMarkdown from "../../../components/IOMarkdown"; import { pinPukHelpUrl } from "../../../config"; import { isCieLoginUatEnabledSelector } from "../../../features/cieLogin/store/selectors"; import { cieFlowForDevServerEnabled } from "../../../features/cieLogin/utils"; @@ -111,9 +111,7 @@ const CiePinScreen = () => { const { present, bottomSheet } = useIOBottomSheetAutoresizableModal({ component: ( - - {I18n.t("bottomSheets.ciePin.content")} - + {canShowButton && ( diff --git a/ts/features/euCovidCert/screens/valid/EuCovidCertValidScreen.tsx b/ts/features/euCovidCert/screens/valid/EuCovidCertValidScreen.tsx index 00b2f64d846..f1f9b280f7d 100644 --- a/ts/features/euCovidCert/screens/valid/EuCovidCertValidScreen.tsx +++ b/ts/features/euCovidCert/screens/valid/EuCovidCertValidScreen.tsx @@ -32,7 +32,6 @@ import { FlashAnimatedComponent, FlashAnimationState } from "../../components/FlashAnimatedComponent"; -import { MarkdownHandleCustomLink } from "../../components/MarkdownHandleCustomLink"; import { navigateToEuCovidCertificateMarkdownDetailsScreen, navigateToEuCovidCertificateQrCodeFullScreen @@ -43,6 +42,7 @@ import { } from "../../types/EUCovidCertificate"; import { captureScreenshot, screenshotOptions } from "../../utils/screenshot"; import { BaseEuCovidCertificateLayout } from "../BaseEuCovidCertificateLayout"; +import IOMarkdown from "../../../../components/IOMarkdown"; type Props = { validCertificate: ValidCertificate; @@ -118,12 +118,7 @@ const EuCovidCertValidComponent = ( )} {props.validCertificate.markdownInfo && ( - - {props.validCertificate.markdownInfo} - + )} From 862a62bfe01d7df3ddc8bc5c847b596305fb85b1 Mon Sep 17 00:00:00 2001 From: Andrea Piai Date: Tue, 26 Nov 2024 17:30:54 +0100 Subject: [PATCH 17/35] Tests --- .../screens/__test__/EUCovidCertValidScreen.test.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/ts/features/euCovidCert/screens/__test__/EUCovidCertValidScreen.test.tsx b/ts/features/euCovidCert/screens/__test__/EUCovidCertValidScreen.test.tsx index 3c4e7f70cc4..02f6fb8d3c9 100644 --- a/ts/features/euCovidCert/screens/__test__/EUCovidCertValidScreen.test.tsx +++ b/ts/features/euCovidCert/screens/__test__/EUCovidCertValidScreen.test.tsx @@ -56,8 +56,6 @@ describe("Test EUCovidCertificateValidScreen", () => { expect( render.component.queryByText(I18n.t("global.buttons.details")) ).toBeNull(); - - expect(render.component.queryByTestId("markdownPreview")).not.toBeNull(); }); it("With completeValidCertificate, the details button and preview markdown should be rendered", () => { const globalState = appReducer(undefined, applicationChangeState("active")); @@ -116,7 +114,6 @@ describe("Test EUCovidCertificateValidScreen", () => { }; expect(spy).toHaveBeenCalledWith(qrCodePayload); } - expect(render.component.queryByTestId("markdownPreview")).not.toBeNull(); }); it("With baseValidCertificate, the header should match the data contained in the certificate", () => { From b0d9a41e706140e0bcd2ddd9a1e57d989ed7934e Mon Sep 17 00:00:00 2001 From: Andrea Piai Date: Thu, 5 Dec 2024 15:37:10 +0100 Subject: [PATCH 18/35] BonusInformationComponent Removed Markdown & MarkdownWebViewComponent --- .eslintignore | 1 - ios/Podfile.lock | 2 +- jest-e2e.config.js | 2 +- jest.config.js | 2 +- jest.config.no.timezone.js | 2 +- jestSetup.js | 7 - package.json | 8 - ts/@types/xss.d.ts | 16 - .../ui/{Markdown => }/LoadingSkeleton.tsx | 2 +- ts/components/ui/Markdown/Markdown.tsx | 200 ----- .../ui/Markdown/MarkdownWebviewComponent.tsx | 91 --- ts/components/ui/Markdown/handlers/link.ts | 23 - ts/components/ui/Markdown/script.ts | 11 - ts/components/ui/Markdown/utils.ts | 146 ---- .../components/BonusInformationComponent.tsx | 43 +- .../components/FimsPrivacyInfo.tsx | 2 +- .../components/FimsSuccessBody.tsx | 2 +- .../components/CardWithMarkdownContent.tsx | 2 +- ts/utils/__tests__/xss.test.ts | 690 ----------------- ts/utils/markdown.ts | 36 - yarn.lock | 693 +----------------- 21 files changed, 19 insertions(+), 1962 deletions(-) delete mode 100644 ts/@types/xss.d.ts rename ts/components/ui/{Markdown => }/LoadingSkeleton.tsx (97%) delete mode 100644 ts/components/ui/Markdown/Markdown.tsx delete mode 100644 ts/components/ui/Markdown/MarkdownWebviewComponent.tsx delete mode 100644 ts/components/ui/Markdown/utils.ts delete mode 100644 ts/utils/__tests__/xss.test.ts delete mode 100644 ts/utils/markdown.ts diff --git a/.eslintignore b/.eslintignore index 247631d1a94..91e02c4fa0b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,3 @@ locales/locales.ts -ts/utils/__tests__/xss.test.ts definitions/* **/*.typegen.ts \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 177ca251397..00b79ab7ed4 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1155,4 +1155,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: c81fe8fa67af4bf6e27653958d177ac176ab0293 -COCOAPODS: 1.14.3 +COCOAPODS: 1.16.2 diff --git a/jest-e2e.config.js b/jest-e2e.config.js index 1c68f9651d5..f87388c1865 100644 --- a/jest-e2e.config.js +++ b/jest-e2e.config.js @@ -1,7 +1,7 @@ module.exports = { preset: "react-native", transformIgnorePatterns: [ - "node_modules/(?!(jest-)?@react-native|react-native|react-navigation|@react-navigation|react-navigation-redux-helpers|react-native-device-info|rn-placeholder|jsbarcode|@pagopa/react-native-cie|react-native-share|jail-monkey|@react-native-community/art|@react-native-community/push-notification-ios|@shopify/react-native-skia|lottie-react-native|@codler|remark|unified|bail|is-plain-obj|trough|vfile|unist-util-stringify-position|mdast-util-from-markdown|mdast-util-to-string|micromark|parse-entities|character-entities|mdast-util-to-markdown|zwitch|longest-streak|@pagopa/io-react-native-zendesk|rn-qr-generator|mixpanel-react-native|@pagopa/io-app-design-system|@sentry/react-native)" + "node_modules/(?!(jest-)?@react-native|react-native|react-navigation|@react-navigation|react-navigation-redux-helpers|react-native-device-info|rn-placeholder|jsbarcode|@pagopa/react-native-cie|react-native-share|jail-monkey|@react-native-community/art|@react-native-community/push-notification-ios|@shopify/react-native-skia|lottie-react-native|@codler|unified|bail|is-plain-obj|trough|vfile|unist-util-stringify-position|mdast-util-from-markdown|mdast-util-to-string|micromark|parse-entities|character-entities|mdast-util-to-markdown|zwitch|longest-streak|@pagopa/io-react-native-zendesk|rn-qr-generator|mixpanel-react-native|@pagopa/io-app-design-system|@sentry/react-native)" ], moduleNameMapper: { "\\.svg": "/ts/__mocks__/svgMock.js" diff --git a/jest.config.js b/jest.config.js index 79df8177e8f..abcb1b5b54d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,7 +1,7 @@ module.exports = { preset: "react-native", transformIgnorePatterns: [ - "node_modules/(?!(jest-)?@react-native|react-native|react-navigation|@react-navigation|react-navigation-redux-helpers|react-native-device-info|rn-placeholder|jsbarcode|@pagopa/react-native-cie|react-native-share|jail-monkey|@react-native-community/art|@react-native-community/push-notification-ios|@shopify/react-native-skia|lottie-react-native|@codler|remark|unified|bail|is-plain-obj|trough|vfile|unist-util-stringify-position|mdast-util-from-markdown|mdast-util-to-string|micromark|parse-entities|character-entities|mdast-util-to-markdown|zwitch|longest-streak|@pagopa/io-react-native-zendesk|rn-qr-generator|mixpanel-react-native|@pagopa/io-app-design-system|uuid|@sentry/react-native)" + "node_modules/(?!(jest-)?@react-native|react-native|react-navigation|@react-navigation|react-navigation-redux-helpers|react-native-device-info|rn-placeholder|jsbarcode|@pagopa/react-native-cie|react-native-share|jail-monkey|@react-native-community/art|@react-native-community/push-notification-ios|@shopify/react-native-skia|lottie-react-native|@codler|unified|bail|is-plain-obj|trough|vfile|unist-util-stringify-position|mdast-util-from-markdown|mdast-util-to-string|micromark|parse-entities|character-entities|mdast-util-to-markdown|zwitch|longest-streak|@pagopa/io-react-native-zendesk|rn-qr-generator|mixpanel-react-native|@pagopa/io-app-design-system|uuid|@sentry/react-native)" ], moduleNameMapper: { "\\.svg": "/ts/__mocks__/svgMock.js" diff --git a/jest.config.no.timezone.js b/jest.config.no.timezone.js index afd55c07774..0fd44ee28a0 100644 --- a/jest.config.no.timezone.js +++ b/jest.config.no.timezone.js @@ -1,7 +1,7 @@ module.exports = { preset: "react-native", transformIgnorePatterns: [ - "node_modules/(?!(jest-)?@react-native|react-native|react-navigation|@react-navigation|react-navigation-redux-helpers|react-native-device-info|rn-placeholder|jsbarcode|@pagopa/react-native-cie|react-native-share|jail-monkey|@react-native-community/art|@react-native-community/push-notification-ios|@shopify/react-native-skia|lottie-react-native|@codler|remark|unified|bail|is-plain-obj|trough|vfile|unist-util-stringify-position|mdast-util-from-markdown|mdast-util-to-string|micromark|parse-entities|character-entities|mdast-util-to-markdown|zwitch|longest-streak|@pagopa/io-react-native-zendesk|rn-qr-generator|mixpanel-react-native|@pagopa/io-app-design-system|uuid|@sentry/react-native)" + "node_modules/(?!(jest-)?@react-native|react-native|react-navigation|@react-navigation|react-navigation-redux-helpers|react-native-device-info|rn-placeholder|jsbarcode|@pagopa/react-native-cie|react-native-share|jail-monkey|@react-native-community/art|@react-native-community/push-notification-ios|@shopify/react-native-skia|lottie-react-native|@codler|unified|bail|is-plain-obj|trough|vfile|unist-util-stringify-position|mdast-util-from-markdown|mdast-util-to-string|micromark|parse-entities|character-entities|mdast-util-to-markdown|zwitch|longest-streak|@pagopa/io-react-native-zendesk|rn-qr-generator|mixpanel-react-native|@pagopa/io-app-design-system|uuid|@sentry/react-native)" ], moduleNameMapper: { "\\.svg": "/ts/__mocks__/svgMock.js" diff --git a/jestSetup.js b/jestSetup.js index bb6ff8384df..71b2233e998 100644 --- a/jestSetup.js +++ b/jestSetup.js @@ -59,13 +59,6 @@ global.fetch = nodeFetch; // eslint-disable-next-line functional/immutable-data global.AbortController = AbortController; -jest.mock("remark-directive", () => jest.fn()); -jest.mock("remark-rehype", () => jest.fn()); -jest.mock("rehype-stringify", () => jest.fn()); -jest.mock("rehype-format", () => jest.fn()); -jest.mock("unist-util-visit", () => jest.fn()); -jest.mock("hastscript", () => jest.fn()); - jest.mock("react-native-device-info", () => mockRNDeviceInfo); // eslint-disable-next-line no-underscore-dangle, functional/immutable-data diff --git a/package.json b/package.json index ea80529caf7..41f58d1d1b9 100644 --- a/package.json +++ b/package.json @@ -146,7 +146,6 @@ "eslint-plugin-react-native": "^4.0.0", "fp-ts": "^2.12.1", "front-matter": "^4.0.2", - "hastscript": "^7.0.2", "hoist-non-react-statics": "^3.0.1", "io-react-native-secure-storage": "^0.1.1", "io-ts": "^2.2.16", @@ -216,11 +215,6 @@ "redux-logger": "3.0.6", "redux-persist": "5.10.0", "redux-saga": "1.1.3", - "rehype-format": "^4.0.0", - "rehype-stringify": "^9.0.2", - "remark": "^14.0.1", - "remark-directive": "^2.0.0", - "remark-rehype": "^9.1.0", "reselect": "4.0.0", "rn-placeholder": "^1.3.3", "rn-qr-generator": "^1.4.0", @@ -229,12 +223,10 @@ "tslib": "^1.9.3", "typed-redux-saga": "^1.4.0", "typesafe-actions": "4.4.2", - "unist-util-visit": "^4.1.0", "url-parse": "^1.5.9", "uuid": "^8.3.2", "validator": "^13.7.0", "xml2js": "^0.5.0", - "xss": "1.0.10", "xstate": "^5" }, "devDependencies": { diff --git a/ts/@types/xss.d.ts b/ts/@types/xss.d.ts deleted file mode 100644 index d283e4c45e1..00000000000 --- a/ts/@types/xss.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -declare module "xss" { - import { FilterXSS } from "xss"; - export type FilterXSSOptions = any; - - /** - * filter xss function - * - * @param {String} html - * @param {Object} options { whiteList, onTag, onTagAttr, onIgnoreTag, onIgnoreTagAttr, safeAttrValue, escapeHtml } - * @return {String} - */ - export function filterXSS(html: string, options?: FilterXSSOptions): string { - const xss = new FilterXSS(options); - return xss.process(html); - } -} diff --git a/ts/components/ui/Markdown/LoadingSkeleton.tsx b/ts/components/ui/LoadingSkeleton.tsx similarity index 97% rename from ts/components/ui/Markdown/LoadingSkeleton.tsx rename to ts/components/ui/LoadingSkeleton.tsx index 7471032ab66..3c96d5b7b57 100644 --- a/ts/components/ui/Markdown/LoadingSkeleton.tsx +++ b/ts/components/ui/LoadingSkeleton.tsx @@ -2,7 +2,7 @@ import React from "react"; import { View } from "react-native"; import Placeholder from "rn-placeholder"; import { IOColors, VSpacer } from "@pagopa/io-app-design-system"; -import I18n from "../../../i18n"; +import I18n from "../../i18n"; type LoadingSkeletonProps = { lines?: number; diff --git a/ts/components/ui/Markdown/Markdown.tsx b/ts/components/ui/Markdown/Markdown.tsx deleted file mode 100644 index 89983dba5c0..00000000000 --- a/ts/components/ui/Markdown/Markdown.tsx +++ /dev/null @@ -1,200 +0,0 @@ -import React, { useCallback, useEffect, useRef, useState } from "react"; -import { - AppState, - AppStateStatus, - InteractionManager, - LayoutAnimation, - Platform, - ScrollView, - StyleProp, - UIManager, - View, - ViewStyle -} from "react-native"; -import WebView from "react-native-webview"; -import { filterXSS } from "xss"; -import { closeInjectedScript } from "../../../utils/webview"; -import { remarkProcessor } from "../../../utils/markdown"; -import { MarkdownWebviewComponent } from "./MarkdownWebviewComponent"; -import { NOTIFY_BODY_HEIGHT_SCRIPT, NOTIFY_LINK_CLICK_SCRIPT } from "./script"; -import { LoadingSkeleton } from "./LoadingSkeleton"; -import { convertOldDemoMarkdownTag, generateHtml } from "./utils"; - -export type MarkdownProps = { - animated?: boolean; - /** - * The code will be inserted in the html body between - * tags. - */ - avoidTextSelection?: true; - children: string; - cssStyle?: string; - extraBodyHeight?: number; - letUserZoom?: boolean; - loadingLines?: number; - onError?: (error: any) => void; - onLinkClicked?: (url: string) => void; - onLoadEnd?: () => void; - /** - * if shouldHandleLink returns true the clicked link will be handled by the Markdown component - * otherwise Markdown will ignore it. If shouldHandleLink is not defined assume () => true - * @param url - */ - shouldHandleLink?: (url: string) => boolean; - testID?: string; - webViewStyle?: StyleProp; - useCustomSortedList?: boolean; -}; - -type InternalState = { - html?: string; - htmlBodyHeight: number; - isLoading: boolean; - webviewKey: number; -}; - -export const Markdown = (props: MarkdownProps) => { - const [internalState, setInternalState] = useState({ - html: undefined, - htmlBodyHeight: 0, - isLoading: true, - webviewKey: 0 - }); - const webViewRef = useRef(null); - const { html, htmlBodyHeight, isLoading, webviewKey } = internalState; - const containerStyle: ViewStyle = { - height: htmlBodyHeight + (props.extraBodyHeight || 0) - }; - const handleLoadEnd = useCallback(() => { - props.onLoadEnd?.(); - setTimeout(() => { - // to avoid yellow box warning - // it's ugly but it works https://github.com/react-native-community/react-native-webview/issues/341#issuecomment-466639820 - webViewRef.current?.injectJavaScript( - closeInjectedScript(NOTIFY_BODY_HEIGHT_SCRIPT) - ); - }, 100); - }, [props]); - - const compileMarkdownAsync = useCallback( - ( - markdown: string, - animated: boolean = false, - onError?: (error: any) => void, - cssStyle?: string, - useCustomSortedList: boolean = false, - avoidTextSelection: boolean = false - ) => { - void InteractionManager.runAfterInteractions(() => { - if (animated) { - // Animate the layout change - // See https://facebook.github.io/react-native/docs/layoutanimation.html - if (UIManager.setLayoutAnimationEnabledExperimental) { - UIManager.setLayoutAnimationEnabledExperimental(true); - } - LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); - } - remarkProcessor.process( - convertOldDemoMarkdownTag( - // sanitize html to prevent xss attacks - filterXSS(markdown, { stripIgnoreTagBody: ["script"] }) - ), - (error: any, file: any) => { - if (error) { - onError?.(error); - return; - } - // The check below on `isLoading` is to set the property back to 'false' - // value when refreshing with metro, since it is set back to the initial - // 'true' value but the underlying MarkdownWebviewComponent does not - // reload its content (the html is recompiled but it does not change), - // thus, not calling the `handleLoadEnd` callback - const generatedHtml = generateHtml( - String(file), - cssStyle, - useCustomSortedList, - avoidTextSelection - ); - setInternalState(currentInternalState => ({ - ...currentInternalState, - isLoading: - currentInternalState.isLoading && - currentInternalState.html !== generatedHtml, - html: generatedHtml - })); - } - ); - }); - }, - [] - ); - - useEffect(() => { - setInternalState(currentInternalState => ({ - ...currentInternalState, - isLoading: true - })); - compileMarkdownAsync( - props.children, - props.animated, - props.onError, - props.cssStyle, - props.useCustomSortedList, - props.avoidTextSelection - ); - - const subscription = AppState.addEventListener( - "change", - (nextAppState: AppStateStatus) => { - if (Platform.OS === "ios" && nextAppState === "active") { - // Reloads the WebView on iOS. Using reload() on the webview - // reference causes the injected javascript to fail - setInternalState(currentInternalState => ({ - ...currentInternalState, - webviewKey: currentInternalState.webviewKey + 1 - })); - } - } - ); - return () => subscription.remove(); - }, [compileMarkdownAsync, props]); - - return ( - <> - {isLoading && ( - - )} - {/* Hide the WebView until we have the htmlBodyHeight */} - {html && ( - - - - setInternalState(currentInternalState => ({ - ...currentInternalState, - isLoading: false - })) - } - setHtmlBodyHeight={(inputHtmlBodyHeight: number) => - setInternalState(currentInternalState => ({ - ...currentInternalState, - htmlBodyHeight: inputHtmlBodyHeight - })) - } - webViewStyle={props.webViewStyle} - onLinkClicked={props.onLinkClicked} - letUserZoom={props.letUserZoom} - testID={props.testID} - /> - - - )} - - ); -}; diff --git a/ts/components/ui/Markdown/MarkdownWebviewComponent.tsx b/ts/components/ui/Markdown/MarkdownWebviewComponent.tsx deleted file mode 100644 index 75be6f68349..00000000000 --- a/ts/components/ui/Markdown/MarkdownWebviewComponent.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { useLinkTo } from "@react-navigation/native"; -import * as E from "fp-ts/lib/Either"; -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import React, { LegacyRef } from "react"; -import { StyleProp, ViewStyle } from "react-native"; -import { WebView } from "react-native-webview"; -import { WebViewMessageEvent } from "react-native-webview/lib/WebViewTypes"; -import { AVOID_ZOOM_JS, closeInjectedScript } from "../../../utils/webview"; -import { handleInternalLink } from "../../../utils/internalLink"; -import { handleLinkMessage, isIoInternalLink } from "./handlers/link"; -import { WebViewMessage } from "./types"; - -type Props = { - injectedJavascript: string; - handleLoadEnd: () => void; - html: string; - webviewKey: number; - webViewRef: LegacyRef; - setLoadingFalse: () => void; - setHtmlBodyHeight: (h: number) => void; - shouldHandleLink?: (link: string) => boolean; - onLinkClicked?: (url: string) => void; - letUserZoom?: boolean; - webViewStyle?: StyleProp; - testID?: string; -}; - -export const MarkdownWebviewComponent = (props: Props) => { - const linkTo = useLinkTo(); - - const handleWebViewMessage = (event: WebViewMessageEvent) => { - const { shouldHandleLink = () => true } = props; - props.setLoadingFalse(); - - // We validate the format of the message with io-ts - const messageOrErrors = WebViewMessage.decode( - JSON.parse(event.nativeEvent.data) - ); - - pipe( - messageOrErrors, - E.map(message => { - switch (message.type) { - case "LINK_MESSAGE": - if (!shouldHandleLink(message.payload.href)) { - break; - } - if (isIoInternalLink(message.payload.href)) { - handleInternalLink(linkTo, message.payload.href); - break; - } - handleLinkMessage(message.payload.href); - pipe( - props.onLinkClicked, - O.fromNullable, - O.map(s => s(message.payload.href)) - ); - break; - - case "RESIZE_MESSAGE": - props.setHtmlBodyHeight(message.payload.height); - break; - } - }) - ); - }; - return ( - - ); -}; diff --git a/ts/components/ui/Markdown/handlers/link.ts b/ts/components/ui/Markdown/handlers/link.ts index 6bb0fb9844a..5bf45bc3b2a 100644 --- a/ts/components/ui/Markdown/handlers/link.ts +++ b/ts/components/ui/Markdown/handlers/link.ts @@ -1,13 +1,10 @@ -import { IOToast } from "@pagopa/io-app-design-system"; import * as E from "fp-ts/lib/Either"; import * as t from "io-ts"; -import I18n from "../../../../i18n"; import { IO_FIMS_LINK_PREFIX, IO_FIMS_LINK_PROTOCOL, IO_INTERNAL_LINK_PREFIX } from "../../../../utils/navigation"; -import { openWebUrl } from "../../../../utils/url"; export const isIoInternalLink = (href: string): boolean => href.startsWith(IO_INTERNAL_LINK_PREFIX); @@ -73,23 +70,3 @@ export const deriveCustomHandledLink = ( new Error(`"${href}" is not recognized as a valid handled link`) ); }; - -/** - * Handles links clicked in the Markdown (webview) component. - */ -export function handleLinkMessage(href: string) { - if (isIoInternalLink(href)) { - return; - } else { - // External urls must be opened with the OS browser. - // FIXME: Whitelist allowed domains: https://www.pivotaltracker.com/story/show/158470128 - openWebUrl(href); - } -} - -// try to open the given url. If it fails an error toast will shown -export function openLink(url: string, customError?: string) { - const error = customError || I18n.t("global.genericError"); - const getErrorToast = () => IOToast.error(error); - openWebUrl(url, getErrorToast); -} diff --git a/ts/components/ui/Markdown/script.ts b/ts/components/ui/Markdown/script.ts index 978b74d4c67..1dccb7dda12 100644 --- a/ts/components/ui/Markdown/script.ts +++ b/ts/components/ui/Markdown/script.ts @@ -30,14 +30,3 @@ document.body.onclick = function(e) { } }; `; - -// Script to notify the height of the body to the react WebView component -export const NOTIFY_BODY_HEIGHT_SCRIPT = ` -const message = { - type: "RESIZE_MESSAGE", - payload: { - height: document.body.scrollHeight - } -}; -window.ReactNativeWebView.postMessage(JSON.stringify(message)); -`; diff --git a/ts/components/ui/Markdown/utils.ts b/ts/components/ui/Markdown/utils.ts deleted file mode 100644 index a233d1f53b7..00000000000 --- a/ts/components/ui/Markdown/utils.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { IOColors, IOFontWeightNumeric } from "@pagopa/io-app-design-system"; -import { Platform } from "react-native"; -import * as RNFS from "react-native-fs"; - -const textColor = IOColors.bluegrey; -const fontSizeBase = 16; -const textLinkWeight = "600" as IOFontWeightNumeric; -const textMessageDetailLinkColor = IOColors.blue; -const toastColor = IOColors.aquaUltraLight; -const brandPrimary = IOColors.blue; - -const OLD_DEMO_TAG_MARKDOWN_REGEX = /^\[demo\]([\s\S]+?)\[\/demo\]\s*\n{2,}/; -export const convertOldDemoMarkdownTag = (markdown: string) => - markdown.replace( - OLD_DEMO_TAG_MARKDOWN_REGEX, - (_, g1: string) => `::div[${g1}]{.io-demo-block}\n` - ); - -export const generateHtml = ( - content: string, - cssStyle?: string, - useCustomSortedList: boolean = false, - avoidTextSelection: boolean = false -) => ` - - - - - - - ${GLOBAL_CSS} - ${cssStyle ? generateInlineCss(cssStyle) : ""} - ${avoidTextSelection ? avoidTextSelectionCSS : ""} - ${useCustomSortedList ? generateCustomFontList : ""} - ${content} - - - `; - -const generateInlineCss = (cssStyle: string) => ``; - -const TITILLIUM_SANSPRO_FONT_PATH = - Platform.OS === "android" - ? "file:///android_asset/fonts/TitilliumSansPro-Regular.otf" - : `${RNFS.MainBundlePath}/TitilliumSansPro-Regular.otf`; - -const TITILLIUM_SANSPRO_BOLD_FONT_PATH = - Platform.OS === "android" - ? "file:///android_asset/fonts/TitilliumSansPro-Bold.otf" - : `${RNFS.MainBundlePath}/TitilliumSansPro-Bold.otf`; - -const GLOBAL_CSS = ` - -`; - -const avoidTextSelectionCSS = ``; - -const generateCustomFontList = ``; diff --git a/ts/features/bonus/common/components/BonusInformationComponent.tsx b/ts/features/bonus/common/components/BonusInformationComponent.tsx index 8502729db12..401dc11cb42 100644 --- a/ts/features/bonus/common/components/BonusInformationComponent.tsx +++ b/ts/features/bonus/common/components/BonusInformationComponent.tsx @@ -29,10 +29,9 @@ import { BonusAvailable } from "../../../../../definitions/content/BonusAvailabl import { BonusAvailableContent } from "../../../../../definitions/content/BonusAvailableContent"; import { LightModalContext } from "../../../../components/ui/LightModal"; import I18n from "../../../../i18n"; -import customVariables from "../../../../theme/variables"; import { getRemoteLocale } from "../../../messages/utils/messages"; import { maybeNotNullyString } from "../../../../utils/strings"; -import { Markdown } from "../../../../components/ui/Markdown/Markdown"; +import IOMarkdown from "../../../../components/IOMarkdown"; import { IOScrollViewWithLargeHeader } from "../../../../components/ui/IOScrollViewWithLargeHeader"; import { useHeaderSecondLevel } from "../../../../hooks/useHeaderSecondLevel"; import TosBonusComponent from "./TosBonusComponent"; @@ -54,24 +53,6 @@ type Props = OwnProps & "contextualHelp" | "contextualHelpMarkdown" | "faqCategories" >; -const CSS_STYLE = ` -body { - font-size: ${customVariables.fontSizeBase}px; - color: ${customVariables.textColorDark} -} - -h4 { - font-size: ${customVariables.fontSize2}px; -} - -img { - width: 100%; -} -`; - -// for long content markdown computed height should be not enough -const extraMarkdownBodyHeight = 20; - const getTosFooter = ( maybeBonusTos: O.Option, maybeRegulationUrl: O.Option<{ url: string; name: string }>, @@ -106,16 +87,13 @@ const getTosFooter = ( // if tos and regulation url is defined // return a markdown footer including both links reference (BPD) rU => ( - - {I18n.t("bonus.termsAndConditionFooter", { + + /> ) ) ) @@ -262,14 +240,13 @@ const BonusInformationComponent = (props: Props) => {

{bonusTypeLocalizedContent.title}

- - {bonusTypeLocalizedContent.subtitle + + + bonusTypeLocalizedContent.content + } + /> {getTosFooter( maybeBonusTos, diff --git a/ts/features/fims/singleSignOn/components/FimsPrivacyInfo.tsx b/ts/features/fims/singleSignOn/components/FimsPrivacyInfo.tsx index a4be01dc420..410a6ecd481 100644 --- a/ts/features/fims/singleSignOn/components/FimsPrivacyInfo.tsx +++ b/ts/features/fims/singleSignOn/components/FimsPrivacyInfo.tsx @@ -1,6 +1,6 @@ import { Label } from "@pagopa/io-app-design-system"; import * as React from "react"; -import { LoadingSkeleton } from "../../../../components/ui/Markdown/LoadingSkeleton"; +import { LoadingSkeleton } from "../../../../components/ui/LoadingSkeleton"; import I18n from "../../../../i18n"; import { openWebUrl } from "../../../../utils/url"; diff --git a/ts/features/fims/singleSignOn/components/FimsSuccessBody.tsx b/ts/features/fims/singleSignOn/components/FimsSuccessBody.tsx index 99ce219bcb3..b2ef5d3b610 100644 --- a/ts/features/fims/singleSignOn/components/FimsSuccessBody.tsx +++ b/ts/features/fims/singleSignOn/components/FimsSuccessBody.tsx @@ -21,7 +21,7 @@ import * as React from "react"; import { StyleSheet, View } from "react-native"; import { ServiceId } from "../../../../../definitions/backend/ServiceId"; import { FooterActions } from "../../../../components/ui/FooterActions"; -import { LoadingSkeleton } from "../../../../components/ui/Markdown/LoadingSkeleton"; +import { LoadingSkeleton } from "../../../../components/ui/LoadingSkeleton"; import I18n from "../../../../i18n"; import { useIODispatch, useIOStore } from "../../../../store/hooks"; import { useIOBottomSheetModal } from "../../../../utils/hooks/bottomSheet"; diff --git a/ts/features/services/details/components/CardWithMarkdownContent.tsx b/ts/features/services/details/components/CardWithMarkdownContent.tsx index 3c4f728d266..1278142f4e9 100644 --- a/ts/features/services/details/components/CardWithMarkdownContent.tsx +++ b/ts/features/services/details/components/CardWithMarkdownContent.tsx @@ -2,7 +2,7 @@ import React, { memo, ReactNode } from "react"; import { StyleSheet, View } from "react-native"; import { useLinkTo } from "@react-navigation/native"; import { IOColors, useIOTheme } from "@pagopa/io-app-design-system"; -import { LoadingSkeleton } from "../../../../components/ui/Markdown/LoadingSkeleton"; +import { LoadingSkeleton } from "../../../../components/ui/LoadingSkeleton"; import IOMarkdown from "../../../../components/IOMarkdown"; import { generateMessagesAndServicesRules } from "../../../../components/IOMarkdown/customRules"; diff --git a/ts/utils/__tests__/xss.test.ts b/ts/utils/__tests__/xss.test.ts deleted file mode 100644 index 9fac8dc967b..00000000000 --- a/ts/utils/__tests__/xss.test.ts +++ /dev/null @@ -1,690 +0,0 @@ -import { filterXSS } from "xss"; -const maliciousPayloads: ReadonlyArray = [ - ``, - ``, - ``, - ``, - ` `, - ``, - ``, - `โ€œ>`, - ``, - ``, - ``, - `โ€˜; alert(1);`, - `โ€˜)alert(1);//`, - ``, - ``, - ``, - ``, - ` `, - ``, - ``, - `click`, - ``, - ` --!>`, - `
x">