From cb333d920955eaf96124f1b054ca2d53f8ef072e Mon Sep 17 00:00:00 2001 From: Andrea Date: Fri, 17 May 2024 15:38:45 +0200 Subject: [PATCH] feat: [IOCOM-823] Chips for Inbox and Archived messages on new DS Messages' Home (#5770) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⚠️ This PR depends on #5766 ⚠️ ## Short description This PR adds the Chip behaviour to the new DS Messages' Home. |Inbox|Archived| |-|-| |![Simulator Screenshot - iPhone 15 - 2024-05-15 at 14 50 34](https://github.com/pagopa/io-app/assets/5150343/9164fd98-6cff-4087-8f0e-5e10f6144d3a)|![Simulator Screenshot - iPhone 15 - 2024-05-15 at 14 50 37](https://github.com/pagopa/io-app/assets/5150343/59ede441-3b3d-4888-8e80-cb835dcdae89)| ## List of changes proposed in this pull request New Messages Home is organised as follows: ``` MessagesHome \_TabNavigationContainer \_InboxTab \_ArchivedTab \_ViewPagerContainer \_MessageList // Inbox \_MessageList // Archive ``` The major challenge is to avoid unnecessary re-renderings in the components included by the`ViewPager`. In order to do so, the shown category (inbox or archive) is stored into redux (`ts/features/messages/store/reducers/allPaginated.ts`), inside the `allPaginated` state (which is not persisted) as a literal which value can be either `inbox` or `archive`. Named `shownCategory`, its value can be retrieved using a selector (`shownMessageCategorySelector`). The `TabNavigationContainer` component uses such value in order to display the selected tab. On every change, it re-renders itself. On a press event, it dispatches an action to trigger the change into redux and it also instructs the `ViewPager` component to scroll to the desired page. This is necessary since the `ViewPager` does not have a method to show the current page based on a changing value (i.e., it needs to be done programmatically). A guard prevents the event logic to be fired if the already selected Tab is pressed. The `ViewPagerContainer` shows the Inbox and Archive message lists using the new `MessageList` component (and specialising it by assigning the proper category). Since there are only two pages, both instances of `MessageList` are rendered upon first ViewPagerContainer loading. In order to avoid unnecessary re-renderings when the user scrolls horizontally between the pages, the underlying `ViewPager` uses the `onPageSelected` event to dispatch the redux action that updates the `shownCategory`. This triggers the re-rendering of `TabNavigationContainer`, keeping the UI aligned. This event is triggered only when the page changes (i.e., there is no need for a "same-page" guard like in `TabNavigationContainer`). Finally, the logic that triggers the first message loading is placed inside the `TabNavigationContainer`, since it is the only component that can use an `useEffect` hook that triggers every time the screen changes. Loading is fired only if the related message section pot (inbox or archived) is `pot.none`. As a last note, this PR also moves legacy messages' screens and legacy messages home's components into related `legacy` folders. ## How to test Enable both new DS and new Messages' Home (from Profile). Check that: - tap on a Chip switches both Chip and View Pager - slide on the View Pager switches both View Pager and Chip - upon first full displaying of each inbox and archive screens, a `reloadAllMessage.request` should be fired (but no more afterwards) - check that the only component that re-renders itself multiple times is `TabNavigationContainer` --- .../__snapshots__/persistedStore.test.ts.snap | 1 + .../messages/components/Home/MessageList.tsx | 22 + .../components/Home/PagerViewContainer.tsx | 39 + .../Home/TabNavigationContainer.tsx | 80 + .../__tests__/Item.test.tsx | 2 +- .../__tests__/TabNavigationContainer.test.tsx | 128 ++ .../__snapshots__/Item.test.tsx.snap | 0 .../TabNavigationContainer.test.tsx.snap | 1450 +++++++++++++++++ .../Home/__tests__/homeUtils.test.ts | 257 +++ .../messages/components/Home/homeUtils.ts | 37 + .../{ => Home/legacy}/EmptyListComponent.tsx | 6 +- .../legacy}/ErrorLoadingComponent.tsx | 10 +- .../{MessageList => Home/legacy}/Item.tsx | 38 +- .../{ => Home/legacy}/ListSelectionBar.tsx | 2 +- .../{ => Home/legacy}/MessageFeedback.tsx | 0 .../{ => Home/legacy}/MessagesArchive.tsx | 12 +- .../{ => Home/legacy}/MessagesInbox.tsx | 12 +- .../{ => Home/legacy}/MessagesSearch.tsx | 6 +- .../{MessageList => Home/legacy}/helpers.tsx | 3 +- .../{MessageList => Home/legacy}/index.tsx | 20 +- .../__test__/EmptyListComponent.test.tsx | 2 +- .../__test__/ListSelectionBar.test.tsx | 2 +- .../components/__test__/MessageList.test.tsx | 2 +- .../__test__/MessagesInbox.test.tsx | 2 +- .../__test__/MessagesSearch.test.tsx | 2 +- .../messages/hooks/useMessageOpening.tsx | 2 +- .../navigation/MessagesHomeTabNavigator.tsx | 2 +- .../messages/navigation/MessagesNavigator.tsx | 4 +- .../messages/screens/MessagesHomeScreen.tsx | 30 +- .../LegacyMessagesHomeScreen.test.tsx | 5 +- .../{ => legacy}/LegacyMessageAttachment.tsx | 16 +- .../LegacyMessageDetailScreen.tsx | 38 +- .../{ => legacy}/LegacyMessagesHomeScreen.tsx | 40 +- .../{ => legacy}/MessageListScreen.tsx | 18 +- .../screens/{ => legacy}/MigratingMessage.tsx | 14 +- ts/features/messages/store/actions/index.ts | 6 + .../reducers/__tests__/allPaginated.test.ts | 264 ++- .../messages/store/reducers/allPaginated.ts | 41 +- ts/navigation/TabNavigator.tsx | 2 +- 39 files changed, 2455 insertions(+), 162 deletions(-) create mode 100644 ts/features/messages/components/Home/MessageList.tsx create mode 100644 ts/features/messages/components/Home/PagerViewContainer.tsx create mode 100644 ts/features/messages/components/Home/TabNavigationContainer.tsx rename ts/features/messages/components/{MessageList => Home}/__tests__/Item.test.tsx (99%) create mode 100644 ts/features/messages/components/Home/__tests__/TabNavigationContainer.test.tsx rename ts/features/messages/components/{MessageList => Home}/__tests__/__snapshots__/Item.test.tsx.snap (100%) create mode 100644 ts/features/messages/components/Home/__tests__/__snapshots__/TabNavigationContainer.test.tsx.snap create mode 100644 ts/features/messages/components/Home/__tests__/homeUtils.test.ts create mode 100644 ts/features/messages/components/Home/homeUtils.ts rename ts/features/messages/components/{ => Home/legacy}/EmptyListComponent.tsx (78%) rename ts/features/messages/components/{MessageList => Home/legacy}/ErrorLoadingComponent.tsx (60%) rename ts/features/messages/components/{MessageList => Home/legacy}/Item.tsx (84%) rename ts/features/messages/components/{ => Home/legacy}/ListSelectionBar.tsx (97%) rename ts/features/messages/components/{ => Home/legacy}/MessageFeedback.tsx (100%) rename ts/features/messages/components/{ => Home/legacy}/MessagesArchive.tsx (89%) rename ts/features/messages/components/{ => Home/legacy}/MessagesInbox.tsx (89%) rename ts/features/messages/components/{ => Home/legacy}/MessagesSearch.tsx (84%) rename ts/features/messages/components/{MessageList => Home/legacy}/helpers.tsx (93%) rename ts/features/messages/components/{MessageList => Home/legacy}/index.tsx (95%) rename ts/features/messages/screens/{ => legacy}/LegacyMessageAttachment.tsx (81%) rename ts/features/messages/screens/{ => legacy}/LegacyMessageDetailScreen.tsx (77%) rename ts/features/messages/screens/{ => legacy}/LegacyMessagesHomeScreen.tsx (79%) rename ts/features/messages/screens/{ => legacy}/MessageListScreen.tsx (78%) rename ts/features/messages/screens/{ => legacy}/MigratingMessage.tsx (88%) diff --git a/ts/boot/__tests__/__snapshots__/persistedStore.test.ts.snap b/ts/boot/__tests__/__snapshots__/persistedStore.test.ts.snap index 268c1e84267..57278305868 100644 --- a/ts/boot/__tests__/__snapshots__/persistedStore.test.ts.snap +++ b/ts/boot/__tests__/__snapshots__/persistedStore.test.ts.snap @@ -64,6 +64,7 @@ Object { "migration": Object { "_tag": "None", }, + "shownCategory": "INBOX", }, "detailsById": Object {}, "downloads": Object {}, diff --git a/ts/features/messages/components/Home/MessageList.tsx b/ts/features/messages/components/Home/MessageList.tsx new file mode 100644 index 00000000000..0bdf1503910 --- /dev/null +++ b/ts/features/messages/components/Home/MessageList.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import { StyleSheet, View } from "react-native"; +import { Body } from "@pagopa/io-app-design-system"; +import { MessageListCategory } from "../../types/messageListCategory"; + +const styles = StyleSheet.create({ + tempPagerViewContainer: { + alignItems: "center", + flex: 1, + justifyContent: "center" + } +}); + +type MessageListProps = { + category: MessageListCategory; +}; + +export const MessageList = ({ category }: MessageListProps) => ( + + {`${category} in sviluppo`} + +); diff --git a/ts/features/messages/components/Home/PagerViewContainer.tsx b/ts/features/messages/components/Home/PagerViewContainer.tsx new file mode 100644 index 00000000000..e2dd672cdda --- /dev/null +++ b/ts/features/messages/components/Home/PagerViewContainer.tsx @@ -0,0 +1,39 @@ +import React, { useCallback } from "react"; +import { NativeSyntheticEvent } from "react-native"; +import PagerView from "react-native-pager-view"; +import { OnPageSelectedEventData } from "react-native-pager-view/lib/typescript/PagerViewNativeComponent"; +import { IOStyles } from "@pagopa/io-app-design-system"; +import { useIODispatch, useIOStore } from "../../../../store/hooks"; +import { setShownMessageCategoryAction } from "../../store/actions"; +import { MessageList } from "./MessageList"; +import { + getMessagesViewPagerInitialPageIndex, + messageViewPageIndexToListCategory +} from "./homeUtils"; + +export const PagerViewContainer = React.forwardRef((_, ref) => { + const dispatch = useIODispatch(); + const store = useIOStore(); + const state = store.getState(); + const initialPageIndex = getMessagesViewPagerInitialPageIndex(state); + const onPagerViewPageSelected = useCallback( + (selectionEvent: NativeSyntheticEvent) => { + const selectedTabIndex = selectionEvent.nativeEvent.position; + const selectedShownCategory = + messageViewPageIndexToListCategory(selectedTabIndex); + dispatch(setShownMessageCategoryAction(selectedShownCategory)); + }, + [dispatch] + ); + return ( + + + + + ); +}); diff --git a/ts/features/messages/components/Home/TabNavigationContainer.tsx b/ts/features/messages/components/Home/TabNavigationContainer.tsx new file mode 100644 index 00000000000..907a1567530 --- /dev/null +++ b/ts/features/messages/components/Home/TabNavigationContainer.tsx @@ -0,0 +1,80 @@ +import React, { useCallback, useEffect } from "react"; +import { StyleSheet, View } from "react-native"; +import PagerView from "react-native-pager-view"; +import { TabItem, TabNavigation } from "@pagopa/io-app-design-system"; +import I18n from "../../../../i18n"; +import { + useIODispatch, + useIOSelector, + useIOStore +} from "../../../../store/hooks"; +import { shownMessageCategorySelector } from "../../store/reducers/allPaginated"; +import { setShownMessageCategoryAction } from "../../store/actions"; +import { + getInitialReloadAllMessagesActionIfNeeded, + messageListCategoryToViewPageIndex, + messageViewPageIndexToListCategory +} from "./homeUtils"; + +const styles = StyleSheet.create({ + tabContainer: { + paddingVertical: 8 + } +}); + +export const TabNavigationContainer = ({ + pagerViewRef +}: { + pagerViewRef: React.MutableRefObject; +}) => { + const dispatch = useIODispatch(); + const store = useIOStore(); + const shownMessageCategory = useIOSelector(shownMessageCategorySelector); + const shownPageIndex = + messageListCategoryToViewPageIndex(shownMessageCategory); + const onTabNavigationItemPressed = useCallback( + (selectedTabIndex: number) => { + if (shownPageIndex !== selectedTabIndex) { + const selectedShownCategory = + messageViewPageIndexToListCategory(selectedTabIndex); + dispatch(setShownMessageCategoryAction(selectedShownCategory)); + pagerViewRef.current?.setPage(selectedTabIndex); + } + }, + [dispatch, pagerViewRef, shownPageIndex] + ); + useEffect(() => { + const state = store.getState(); + const reloadAllMessagesActionOrUndefined = + getInitialReloadAllMessagesActionIfNeeded(state); + if (reloadAllMessagesActionOrUndefined) { + dispatch(reloadAllMessagesActionOrUndefined); + } + }, [dispatch, shownMessageCategory, store]); + return ( + + + + + + + ); +}; diff --git a/ts/features/messages/components/MessageList/__tests__/Item.test.tsx b/ts/features/messages/components/Home/__tests__/Item.test.tsx similarity index 99% rename from ts/features/messages/components/MessageList/__tests__/Item.test.tsx rename to ts/features/messages/components/Home/__tests__/Item.test.tsx index 5f553d28241..2a150282c86 100644 --- a/ts/features/messages/components/MessageList/__tests__/Item.test.tsx +++ b/ts/features/messages/components/Home/__tests__/Item.test.tsx @@ -6,7 +6,7 @@ import { MessageCategory } from "../../../../../../definitions/backend/MessageCa import { TagEnum } from "../../../../../../definitions/backend/MessageCategoryBase"; import { TagEnum as TagEnumPN } from "../../../../../../definitions/backend/MessageCategoryPN"; import { successReloadMessagesPayload } from "../../../__mocks__/messages"; -import Item from "../Item"; +import Item from "../legacy/Item"; import { GlobalState } from "../../../../../store/reducers/types"; import { applicationChangeState } from "../../../../../store/actions/application"; import { appReducer } from "../../../../../store/reducers"; diff --git a/ts/features/messages/components/Home/__tests__/TabNavigationContainer.test.tsx b/ts/features/messages/components/Home/__tests__/TabNavigationContainer.test.tsx new file mode 100644 index 00000000000..d0b4bb4d89c --- /dev/null +++ b/ts/features/messages/components/Home/__tests__/TabNavigationContainer.test.tsx @@ -0,0 +1,128 @@ +import React from "react"; +import { createStore } from "redux"; +import PagerView from "react-native-pager-view"; +import { fireEvent } from "@testing-library/react-native"; +import { applicationChangeState } from "../../../../../store/actions/application"; +import { preferencesDesignSystemSetEnabled } from "../../../../../store/actions/persistedPreferences"; +import { appReducer } from "../../../../../store/reducers"; +import { renderScreenWithNavigationStoreContext } from "../../../../../utils/testWrapper"; +import { MESSAGES_ROUTES } from "../../../navigation/routes"; +import { TabNavigationContainer } from "../TabNavigationContainer"; +import { MessageListCategory } from "../../../types/messageListCategory"; +import { + reloadAllMessages, + setShownMessageCategoryAction +} from "../../../store/actions"; +import { pageSize } from "../../../../../config"; + +const mockDispatch = jest.fn(); +jest.mock("react-redux", () => ({ + ...jest.requireActual("react-redux"), + useDispatch: () => mockDispatch +})); + +describe("TabNavigationContainer", () => { + beforeEach(() => { + jest.resetAllMocks(); + jest.clearAllMocks(); + }); + it("should match snapshot when shownCategory is INBOX", () => { + const screen = renderScreen("INBOX"); + expect(screen.toJSON()).toMatchSnapshot(); + }); + it("should match snapshot when shownCategory is ARCHIVE", () => { + const screen = renderScreen("ARCHIVE"); + expect(screen.toJSON()).toMatchSnapshot(); + }); + it("when displaying INBOX, pot.none inbox, should dispatch reloadAllMessages.request", () => { + renderScreen("INBOX"); + expect(mockDispatch.mock.calls[0][0]).toStrictEqual( + reloadAllMessages.request({ + pageSize, + filter: { getArchived: false } + }) + ); + }); + it("when displaying INBOX and ARCHIVE chips is pressed, it should dispatch setShownMessageCategoryAction and trigger pagerViewRef ", () => { + const setPageMock = jest.fn(); + const screen = renderScreen("INBOX", setPageMock); + const archivePressableComponent = screen.getByTestId( + "home_tab_item_archive" + ); + expect(archivePressableComponent).toBeDefined(); + fireEvent.press(archivePressableComponent); + expect(mockDispatch.mock.calls[1][0]).toStrictEqual( + setShownMessageCategoryAction("ARCHIVE") + ); + expect(setPageMock.mock.calls[0][0]).toStrictEqual(1); + }); + it("when displaying INBOX and INBOX chips is pressed, it should NOT dispatch setShownMessageCategoryAction and NOT trigger pagerViewRef ", () => { + const setPageMock = jest.fn(); + const screen = renderScreen("INBOX", setPageMock); + const inboxPressableComponent = screen.getByTestId("home_tab_item_inbox"); + expect(inboxPressableComponent).toBeDefined(); + fireEvent.press(inboxPressableComponent); + expect(mockDispatch.mock.calls[1]).toBeUndefined(); + expect(setPageMock.mock.calls[0]).toBeUndefined(); + }); + it("when displaying ARCHIVE, pot.none archive, should dispatch reloadAllMessages.request", () => { + renderScreen("ARCHIVE"); + expect(mockDispatch.mock.calls[0][0]).toStrictEqual( + reloadAllMessages.request({ + pageSize, + filter: { getArchived: true } + }) + ); + }); + it("when displaying INBOX and INBOX chips is pressed, it should dispatch setShownMessageCategoryAction and trigger pagerViewRef ", () => { + const setPageMock = jest.fn(); + const screen = renderScreen("ARCHIVE", setPageMock); + const inboxPressableComponent = screen.getByTestId("home_tab_item_inbox"); + expect(inboxPressableComponent).toBeDefined(); + fireEvent.press(inboxPressableComponent); + expect(mockDispatch.mock.calls[1][0]).toStrictEqual( + setShownMessageCategoryAction("INBOX") + ); + expect(setPageMock.mock.calls[0][0]).toStrictEqual(0); + }); + it("when displaying INBOX and ARCHIVE chips is pressed, it should NOT dispatch setShownMessageCategoryAction and NOT trigger pagerViewRef ", () => { + const setPageMock = jest.fn(); + const screen = renderScreen("ARCHIVE", setPageMock); + const archivePressableComponent = screen.getByTestId( + "home_tab_item_archive" + ); + expect(archivePressableComponent).toBeDefined(); + fireEvent.press(archivePressableComponent); + expect(mockDispatch.mock.calls[1]).toBeUndefined(); + expect(setPageMock.mock.calls[0]).toBeUndefined(); + }); +}); + +const renderScreen = ( + shownCategory: MessageListCategory, + setPageMock: jest.Mock = jest.fn() +) => { + const initialState = appReducer(undefined, applicationChangeState("active")); + const designSystemState = appReducer( + initialState, + preferencesDesignSystemSetEnabled({ isDesignSystemEnabled: true }) + ); + const finalState = appReducer( + designSystemState, + setShownMessageCategoryAction(shownCategory) + ); + const store = createStore(appReducer, finalState as any); + + const mockPaferViewRef = { + current: { + setPage: (_: number) => setPageMock(_) + } as PagerView + }; + + return renderScreenWithNavigationStoreContext( + () => , + MESSAGES_ROUTES.MESSAGES_HOME, + {}, + store + ); +}; diff --git a/ts/features/messages/components/MessageList/__tests__/__snapshots__/Item.test.tsx.snap b/ts/features/messages/components/Home/__tests__/__snapshots__/Item.test.tsx.snap similarity index 100% rename from ts/features/messages/components/MessageList/__tests__/__snapshots__/Item.test.tsx.snap rename to ts/features/messages/components/Home/__tests__/__snapshots__/Item.test.tsx.snap diff --git a/ts/features/messages/components/Home/__tests__/__snapshots__/TabNavigationContainer.test.tsx.snap b/ts/features/messages/components/Home/__tests__/__snapshots__/TabNavigationContainer.test.tsx.snap new file mode 100644 index 00000000000..783672cc45c --- /dev/null +++ b/ts/features/messages/components/Home/__tests__/__snapshots__/TabNavigationContainer.test.tsx.snap @@ -0,0 +1,1450 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TabNavigationContainer should match snapshot when shownCategory is ARCHIVE 1`] = ` + + + + + + + + + + + + + + + MESSAGES_HOME + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Inbox + + + + + + + + + + + + + + + Archived + + + + + + + + + + + + + + + + + + +`; + +exports[`TabNavigationContainer should match snapshot when shownCategory is INBOX 1`] = ` + + + + + + + + + + + + + + + MESSAGES_HOME + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Inbox + + + + + + + + + + + + + + + Archived + + + + + + + + + + + + + + + + + + +`; diff --git a/ts/features/messages/components/Home/__tests__/homeUtils.test.ts b/ts/features/messages/components/Home/__tests__/homeUtils.test.ts new file mode 100644 index 00000000000..f7849c171ca --- /dev/null +++ b/ts/features/messages/components/Home/__tests__/homeUtils.test.ts @@ -0,0 +1,257 @@ +import { pot } from "@pagopa/ts-commons"; +import { ActionType } from "typesafe-actions"; +import { GlobalState } from "../../../../../store/reducers/types"; +import { MessageListCategory } from "../../../types/messageListCategory"; +import { + MessagePage, + MessagePagePot +} from "../../../store/reducers/allPaginated"; +import { + getInitialReloadAllMessagesActionIfNeeded, + getMessagesViewPagerInitialPageIndex, + messageListCategoryToViewPageIndex, + messageViewPageIndexToListCategory +} from "../homeUtils"; +import { pageSize } from "../../../../../config"; +import { Action } from "../../../../../store/actions/types"; +import { reloadAllMessages } from "../../../store/actions"; + +const createGlobalState = ( + archiveData: MessagePagePot, + inboxData: MessagePagePot, + shownCategory: MessageListCategory +) => + ({ + entities: { + messages: { + allPaginated: { + archive: { + data: archiveData + }, + inbox: { + data: inboxData + }, + shownCategory + } + } + } + } as GlobalState); + +const checkReturnedAction = (action?: Action, getArchived: boolean = false) => { + expect(action).not.toBeUndefined(); + expect(action?.type).toBe("MESSAGES_RELOAD_REQUEST"); + const reloadAllMessagesRequest = action as ActionType< + typeof reloadAllMessages.request + >; + expect(reloadAllMessagesRequest?.payload.pageSize).toBe(pageSize); + expect(reloadAllMessagesRequest?.payload.filter.getArchived).toBe( + getArchived + ); +}; + +describe("getInitialReloadAllMessagesActionIfNeeded", () => { + it("should return reloadAllMessages.request when showing inbox with pot.none inbox", () => { + const globalState = createGlobalState( + pot.some({} as MessagePage), + pot.none, + "INBOX" + ); + const reloadAllMessagesRequest = + getInitialReloadAllMessagesActionIfNeeded(globalState); + checkReturnedAction(reloadAllMessagesRequest); + }); + it("should return undefined when showing inbox with pot.noneLoading inbox", () => { + const globalState = createGlobalState( + pot.some({} as MessagePage), + pot.noneLoading, + "INBOX" + ); + const reloadAllMessagesRequest = + getInitialReloadAllMessagesActionIfNeeded(globalState); + expect(reloadAllMessagesRequest).toBeUndefined(); + }); + it("should return undefined when showing inbox with pot.noneUpdating inbox", () => { + const globalState = createGlobalState( + pot.some({} as MessagePage), + pot.noneUpdating({} as MessagePage), + "INBOX" + ); + const reloadAllMessagesRequest = + getInitialReloadAllMessagesActionIfNeeded(globalState); + expect(reloadAllMessagesRequest).toBeUndefined(); + }); + it("should return undefined when showing inbox with pot.noneError inbox", () => { + const globalState = createGlobalState( + pot.some({} as MessagePage), + pot.noneError(""), + "INBOX" + ); + const reloadAllMessagesRequest = + getInitialReloadAllMessagesActionIfNeeded(globalState); + expect(reloadAllMessagesRequest).toBeUndefined(); + }); + it("should return undefined when showing inbox with pot.some inbox", () => { + const globalState = createGlobalState( + pot.some({} as MessagePage), + pot.some({} as MessagePage), + "INBOX" + ); + const reloadAllMessagesRequest = + getInitialReloadAllMessagesActionIfNeeded(globalState); + expect(reloadAllMessagesRequest).toBeUndefined(); + }); + it("should return undefined when showing inbox with pot.someLoading inbox", () => { + const globalState = createGlobalState( + pot.some({} as MessagePage), + pot.someLoading({} as MessagePage), + "INBOX" + ); + const reloadAllMessagesRequest = + getInitialReloadAllMessagesActionIfNeeded(globalState); + expect(reloadAllMessagesRequest).toBeUndefined(); + }); + it("should return undefined when showing inbox with pot.someUpdating inbox", () => { + const globalState = createGlobalState( + pot.some({} as MessagePage), + pot.someUpdating({} as MessagePage, {} as MessagePage), + "INBOX" + ); + const reloadAllMessagesRequest = + getInitialReloadAllMessagesActionIfNeeded(globalState); + expect(reloadAllMessagesRequest).toBeUndefined(); + }); + it("should return undefined when showing inbox with pot.someError inbox", () => { + const globalState = createGlobalState( + pot.some({} as MessagePage), + pot.someError({} as MessagePage, ""), + "INBOX" + ); + const reloadAllMessagesRequest = + getInitialReloadAllMessagesActionIfNeeded(globalState); + expect(reloadAllMessagesRequest).toBeUndefined(); + }); + + it("should return reloadAllMessages.request when showing archive with pot.none archive", () => { + const globalState = createGlobalState( + pot.none, + pot.some({} as MessagePage), + "ARCHIVE" + ); + const reloadAllMessagesRequest = + getInitialReloadAllMessagesActionIfNeeded(globalState); + checkReturnedAction(reloadAllMessagesRequest, true); + }); + it("should return undefined when showing archive with pot.noneLoading archive", () => { + const globalState = createGlobalState( + pot.noneLoading, + pot.some({} as MessagePage), + "ARCHIVE" + ); + const reloadAllMessagesRequest = + getInitialReloadAllMessagesActionIfNeeded(globalState); + expect(reloadAllMessagesRequest).toBeUndefined(); + }); + it("should return undefined when showing archive with pot.noneUpdating archive", () => { + const globalState = createGlobalState( + pot.noneUpdating({} as MessagePage), + pot.some({} as MessagePage), + "ARCHIVE" + ); + const reloadAllMessagesRequest = + getInitialReloadAllMessagesActionIfNeeded(globalState); + expect(reloadAllMessagesRequest).toBeUndefined(); + }); + it("should return undefined when showing archive with pot.noneError archive", () => { + const globalState = createGlobalState( + pot.noneError(""), + pot.some({} as MessagePage), + "ARCHIVE" + ); + const reloadAllMessagesRequest = + getInitialReloadAllMessagesActionIfNeeded(globalState); + expect(reloadAllMessagesRequest).toBeUndefined(); + }); + it("should return undefined when showing archive with pot.some archive", () => { + const globalState = createGlobalState( + pot.some({} as MessagePage), + pot.some({} as MessagePage), + "ARCHIVE" + ); + const reloadAllMessagesRequest = + getInitialReloadAllMessagesActionIfNeeded(globalState); + expect(reloadAllMessagesRequest).toBeUndefined(); + }); + it("should return undefined when showing archive with pot.someLoading archive", () => { + const globalState = createGlobalState( + pot.someLoading({} as MessagePage), + pot.some({} as MessagePage), + "ARCHIVE" + ); + const reloadAllMessagesRequest = + getInitialReloadAllMessagesActionIfNeeded(globalState); + expect(reloadAllMessagesRequest).toBeUndefined(); + }); + it("should return undefined when showing archive with pot.someUpdating archive", () => { + const globalState = createGlobalState( + pot.someUpdating({} as MessagePage, {} as MessagePage), + pot.some({} as MessagePage), + "ARCHIVE" + ); + const reloadAllMessagesRequest = + getInitialReloadAllMessagesActionIfNeeded(globalState); + expect(reloadAllMessagesRequest).toBeUndefined(); + }); + it("should return undefined when showing archive with pot.someError archive", () => { + const globalState = createGlobalState( + pot.someError({} as MessagePage, ""), + pot.some({} as MessagePage), + "ARCHIVE" + ); + const reloadAllMessagesRequest = + getInitialReloadAllMessagesActionIfNeeded(globalState); + expect(reloadAllMessagesRequest).toBeUndefined(); + }); +}); + +describe("getMessagesViewPagerInitialPageIndex", () => { + it("should return 1 when shownCategory is ARCHIVED", () => { + const globalState = createGlobalState(pot.none, pot.none, "ARCHIVE"); + const pageIndex = getMessagesViewPagerInitialPageIndex(globalState); + expect(pageIndex).toBe(1); + }); + it("should return 0 when shownCategory is INBOX", () => { + const globalState = createGlobalState(pot.none, pot.none, "INBOX"); + const pageIndex = getMessagesViewPagerInitialPageIndex(globalState); + expect(pageIndex).toBe(0); + }); +}); + +describe("messageListCategoryToViewPageIndex", () => { + it("should return 0 for INBOX", () => { + const pageIndex = messageListCategoryToViewPageIndex("INBOX"); + expect(pageIndex).toBe(0); + }); + it("should return 1 for ARCHIVE", () => { + const pageIndex = messageListCategoryToViewPageIndex("ARCHIVE"); + expect(pageIndex).toBe(1); + }); +}); + +describe("messageViewPageIndexToListCategory", () => { + it("should return ARCHIVE when index is 1", () => { + const listCategory = messageViewPageIndexToListCategory(1); + expect(listCategory).toBe("ARCHIVE"); + }); + it("should return INBOX when index is 0", () => { + const listCategory = messageViewPageIndexToListCategory(0); + expect(listCategory).toBe("INBOX"); + }); + it("should return INBOX when index is -1", () => { + const listCategory = messageViewPageIndexToListCategory(-1); + expect(listCategory).toBe("INBOX"); + }); + it("should return INBOX when index is 2", () => { + const listCategory = messageViewPageIndexToListCategory(2); + expect(listCategory).toBe("INBOX"); + }); +}); diff --git a/ts/features/messages/components/Home/homeUtils.ts b/ts/features/messages/components/Home/homeUtils.ts new file mode 100644 index 00000000000..4fbe377e472 --- /dev/null +++ b/ts/features/messages/components/Home/homeUtils.ts @@ -0,0 +1,37 @@ +import { ActionType } from "typesafe-actions"; +import { GlobalState } from "../../../../store/reducers/types"; +import { reloadAllMessages } from "../../store/actions"; +import { pageSize } from "../../../../config"; +import { MessageListCategory } from "../../types/messageListCategory"; + +export const getInitialReloadAllMessagesActionIfNeeded = ( + state: GlobalState +): ActionType | undefined => { + const allPaginatedState = state.entities.messages.allPaginated; + const shownMessagesCategory = allPaginatedState.shownCategory; + const isShowingArchivedMessages = shownMessagesCategory === "ARCHIVE"; + const messagesCategoryPot = isShowingArchivedMessages + ? allPaginatedState.archive.data + : allPaginatedState.inbox.data; + if (messagesCategoryPot.kind === "PotNone") { + return reloadAllMessages.request({ + pageSize, + filter: { getArchived: isShowingArchivedMessages } + }); + } + + return undefined; +}; + +export const getMessagesViewPagerInitialPageIndex = (state: GlobalState) => { + const shownMessageCategory = + state.entities.messages.allPaginated.shownCategory; + return messageListCategoryToViewPageIndex(shownMessageCategory); +}; + +export const messageListCategoryToViewPageIndex = ( + category: MessageListCategory +) => (category === "ARCHIVE" ? 1 : 0); +export const messageViewPageIndexToListCategory = ( + pageIndex: number +): MessageListCategory => (pageIndex === 1 ? "ARCHIVE" : "INBOX"); diff --git a/ts/features/messages/components/EmptyListComponent.tsx b/ts/features/messages/components/Home/legacy/EmptyListComponent.tsx similarity index 78% rename from ts/features/messages/components/EmptyListComponent.tsx rename to ts/features/messages/components/Home/legacy/EmptyListComponent.tsx index ddbfee141d2..bae0d42cd3d 100644 --- a/ts/features/messages/components/EmptyListComponent.tsx +++ b/ts/features/messages/components/Home/legacy/EmptyListComponent.tsx @@ -1,9 +1,9 @@ import React from "react"; import { View, StyleSheet } from "react-native"; import { VSpacer, IOPictograms, Pictogram } from "@pagopa/io-app-design-system"; -import customVariables from "../../../theme/variables"; -import { Body } from "../../../components/core/typography/Body"; -import { IOStyles } from "../../../components/core/variables/IOStyles"; +import customVariables from "../../../../../theme/variables"; +import { Body } from "../../../../../components/core/typography/Body"; +import { IOStyles } from "../../../../../components/core/variables/IOStyles"; const styles = StyleSheet.create({ view: { diff --git a/ts/features/messages/components/MessageList/ErrorLoadingComponent.tsx b/ts/features/messages/components/Home/legacy/ErrorLoadingComponent.tsx similarity index 60% rename from ts/features/messages/components/MessageList/ErrorLoadingComponent.tsx rename to ts/features/messages/components/Home/legacy/ErrorLoadingComponent.tsx index aaffe93d111..40c8c76e616 100644 --- a/ts/features/messages/components/MessageList/ErrorLoadingComponent.tsx +++ b/ts/features/messages/components/Home/legacy/ErrorLoadingComponent.tsx @@ -1,10 +1,10 @@ import { VSpacer } from "@pagopa/io-app-design-system"; import * as React from "react"; import { Image, StyleSheet, View } from "react-native"; -import { Body } from "../../../../components/core/typography/Body"; -import { IOStyles } from "../../../../components/core/variables/IOStyles"; -import I18n from "../../../../i18n"; -import customVariables from "../../../../theme/variables"; +import { Body } from "../../../../../components/core/typography/Body"; +import { IOStyles } from "../../../../../components/core/variables/IOStyles"; +import I18n from "../../../../../i18n"; +import customVariables from "../../../../../theme/variables"; const styles = StyleSheet.create({ view: { @@ -17,7 +17,7 @@ export const ErrorLoadingComponent = () => ( {I18n.t("messages.loadingErrorTitle")} diff --git a/ts/features/messages/components/MessageList/Item.tsx b/ts/features/messages/components/Home/legacy/Item.tsx similarity index 84% rename from ts/features/messages/components/MessageList/Item.tsx rename to ts/features/messages/components/Home/legacy/Item.tsx index a8dfa18ecea..c8305815afb 100644 --- a/ts/features/messages/components/MessageList/Item.tsx +++ b/ts/features/messages/components/Home/legacy/Item.tsx @@ -3,28 +3,28 @@ import * as O from "fp-ts/lib/Option"; import React from "react"; import { View, StyleSheet } from "react-native"; import { IOColors, Icon, HSpacer } from "@pagopa/io-app-design-system"; -import { MessageCategory } from "../../../../../definitions/backend/MessageCategory"; -import { TagEnum as TagEnumPN } from "../../../../../definitions/backend/MessageCategoryPN"; -import { ServicePublic } from "../../../../../definitions/backend/ServicePublic"; -import PnMessage from "../../../../../img/features/messages/pn_message_badge.svg"; -import I18n from "../../../../i18n"; -import { UIMessage } from "../../types"; -import customVariables from "../../../../theme/variables"; +import { MessageCategory } from "../../../../../../definitions/backend/MessageCategory"; +import { TagEnum as TagEnumPN } from "../../../../../../definitions/backend/MessageCategoryPN"; +import { ServicePublic } from "../../../../../../definitions/backend/ServicePublic"; +import PnMessage from "../../../../../../img/features/messages/pn_message_badge.svg"; +import I18n from "../../../../../i18n"; +import { UIMessage } from "../../../types"; +import customVariables from "../../../../../theme/variables"; import { convertDateToWordDistance, convertReceivedDateToAccessible -} from "../../utils/convertDateToWordDistance"; -import { IOBadge } from "../../../../components/core/IOBadge"; -import { Body } from "../../../../components/core/typography/Body"; -import { H3 } from "../../../../components/core/typography/H3"; -import { H5 } from "../../../../components/core/typography/H5"; -import { Label } from "../../../../components/core/typography/Label"; -import { IOStyles } from "../../../../components/core/variables/IOStyles"; -import { BadgeComponent } from "../../../../components/screens/BadgeComponent"; -import TouchableDefaultOpacity from "../../../../components/TouchableDefaultOpacity"; -import { useIOSelector } from "../../../../store/hooks"; -import { isNoticePaidSelector } from "../../../../store/reducers/entities/payments"; -import { isPnEnabledSelector } from "../../../../store/reducers/backendStatus"; +} from "../../../utils/convertDateToWordDistance"; +import { IOBadge } from "../../../../../components/core/IOBadge"; +import { Body } from "../../../../../components/core/typography/Body"; +import { H3 } from "../../../../../components/core/typography/H3"; +import { H5 } from "../../../../../components/core/typography/H5"; +import { Label } from "../../../../../components/core/typography/Label"; +import { IOStyles } from "../../../../../components/core/variables/IOStyles"; +import { BadgeComponent } from "../../../../../components/screens/BadgeComponent"; +import TouchableDefaultOpacity from "../../../../../components/TouchableDefaultOpacity"; +import { useIOSelector } from "../../../../../store/hooks"; +import { isNoticePaidSelector } from "../../../../../store/reducers/entities/payments"; +import { isPnEnabledSelector } from "../../../../../store/reducers/backendStatus"; const ICON_WIDTH = 24; diff --git a/ts/features/messages/components/ListSelectionBar.tsx b/ts/features/messages/components/Home/legacy/ListSelectionBar.tsx similarity index 97% rename from ts/features/messages/components/ListSelectionBar.tsx rename to ts/features/messages/components/Home/legacy/ListSelectionBar.tsx index 254df042309..0ccfaae8324 100644 --- a/ts/features/messages/components/ListSelectionBar.tsx +++ b/ts/features/messages/components/Home/legacy/ListSelectionBar.tsx @@ -5,7 +5,7 @@ import { ButtonSolid, IOColors } from "@pagopa/io-app-design-system"; -import I18n from "../../../i18n"; +import I18n from "../../../../../i18n"; const styles = StyleSheet.create({ buttonBar: { diff --git a/ts/features/messages/components/MessageFeedback.tsx b/ts/features/messages/components/Home/legacy/MessageFeedback.tsx similarity index 100% rename from ts/features/messages/components/MessageFeedback.tsx rename to ts/features/messages/components/Home/legacy/MessageFeedback.tsx diff --git a/ts/features/messages/components/MessagesArchive.tsx b/ts/features/messages/components/Home/legacy/MessagesArchive.tsx similarity index 89% rename from ts/features/messages/components/MessagesArchive.tsx rename to ts/features/messages/components/Home/legacy/MessagesArchive.tsx index 948a181e4c2..f6877a41658 100644 --- a/ts/features/messages/components/MessagesArchive.tsx +++ b/ts/features/messages/components/Home/legacy/MessagesArchive.tsx @@ -3,14 +3,14 @@ import React, { useCallback } from "react"; import { View, StyleSheet } from "react-native"; import { pipe } from "fp-ts/lib/function"; -import I18n from "../../../i18n"; -import { UIMessage } from "../types"; +import I18n from "../../../../../i18n"; +import { UIMessage } from "../../../types"; -import { useItemsSelection } from "../hooks/useItemsSelection"; -import { IOStyles } from "../../../components/core/variables/IOStyles"; +import { useItemsSelection } from "../../../hooks/useItemsSelection"; +import { IOStyles } from "../../../../../components/core/variables/IOStyles"; import ListSelectionBar from "./ListSelectionBar"; import { EmptyListComponent } from "./EmptyListComponent"; -import MessageList from "./MessageList"; +import MessageList from "."; const styles = StyleSheet.create({ listWrapper: { @@ -56,7 +56,7 @@ const MessagesArchive = ({ navigateToMessageDetail(message); } }, - [selectedItems] + [navigateToMessageDetail, selectedItems, toggleItem] ); const onLongPressItem = (id: string) => { diff --git a/ts/features/messages/components/MessagesInbox.tsx b/ts/features/messages/components/Home/legacy/MessagesInbox.tsx similarity index 89% rename from ts/features/messages/components/MessagesInbox.tsx rename to ts/features/messages/components/Home/legacy/MessagesInbox.tsx index 2fc33823758..d51358a1554 100644 --- a/ts/features/messages/components/MessagesInbox.tsx +++ b/ts/features/messages/components/Home/legacy/MessagesInbox.tsx @@ -3,15 +3,15 @@ import React, { useCallback } from "react"; import { View, StyleSheet } from "react-native"; import { pipe } from "fp-ts/lib/function"; -import I18n from "../../../i18n"; -import { UIMessage } from "../types"; +import I18n from "../../../../../i18n"; +import { UIMessage } from "../../../types"; -import { UaDonationsBanner } from "../../uaDonations/components/UaDonationsBanner"; -import { useItemsSelection } from "../hooks/useItemsSelection"; -import { IOStyles } from "../../../components/core/variables/IOStyles"; +import { UaDonationsBanner } from "../../../../uaDonations/components/UaDonationsBanner"; +import { useItemsSelection } from "../../../hooks/useItemsSelection"; +import { IOStyles } from "../../../../../components/core/variables/IOStyles"; import ListSelectionBar from "./ListSelectionBar"; import { EmptyListComponent } from "./EmptyListComponent"; -import MessageList from "./MessageList"; +import MessageList from "."; const styles = StyleSheet.create({ listWrapper: { diff --git a/ts/features/messages/components/MessagesSearch.tsx b/ts/features/messages/components/Home/legacy/MessagesSearch.tsx similarity index 84% rename from ts/features/messages/components/MessagesSearch.tsx rename to ts/features/messages/components/Home/legacy/MessagesSearch.tsx index bd0ee803422..57f1bccd4cf 100644 --- a/ts/features/messages/components/MessagesSearch.tsx +++ b/ts/features/messages/components/Home/legacy/MessagesSearch.tsx @@ -1,9 +1,9 @@ import React from "react"; import { View, StyleSheet } from "react-native"; -import { UIMessage } from "../types"; -import { isTextIncludedCaseInsensitive } from "../../../utils/strings"; -import { SearchNoResultMessage } from "../../../components/search/SearchNoResultMessage"; +import { UIMessage } from "../../../types"; +import { isTextIncludedCaseInsensitive } from "../../../../../utils/strings"; +import { SearchNoResultMessage } from "../../../../../components/search/SearchNoResultMessage"; const styles = StyleSheet.create({ listWrapper: { diff --git a/ts/features/messages/components/MessageList/helpers.tsx b/ts/features/messages/components/Home/legacy/helpers.tsx similarity index 93% rename from ts/features/messages/components/MessageList/helpers.tsx rename to ts/features/messages/components/Home/legacy/helpers.tsx index f86a29af7d2..be5a91148cb 100644 --- a/ts/features/messages/components/MessageList/helpers.tsx +++ b/ts/features/messages/components/Home/legacy/helpers.tsx @@ -1,7 +1,6 @@ import React from "react"; import { Animated, FlatList } from "react-native"; - -import ItemSeparatorComponent from "../../../../components/ItemSeparatorComponent"; +import ItemSeparatorComponent from "../../../../../components/ItemSeparatorComponent"; import { ErrorLoadingComponent } from "./ErrorLoadingComponent"; export const ITEM_HEIGHT = 114; diff --git a/ts/features/messages/components/MessageList/index.tsx b/ts/features/messages/components/Home/legacy/index.tsx similarity index 95% rename from ts/features/messages/components/MessageList/index.tsx rename to ts/features/messages/components/Home/legacy/index.tsx index 377e6e2ede2..57caf36dc94 100644 --- a/ts/features/messages/components/MessageList/index.tsx +++ b/ts/features/messages/components/Home/legacy/index.tsx @@ -14,21 +14,21 @@ import { Vibration } from "react-native"; import { connect } from "react-redux"; -import { maximumItemsFromAPI, pageSize } from "../../../../config"; -import { useTabItemPressWhenScreenActive } from "../../../../hooks/useTabItemPressWhenScreenActive"; -import I18n from "../../../../i18n"; -import { Dispatch } from "../../../../store/actions/types"; -import { GlobalState } from "../../../../store/reducers/types"; +import { maximumItemsFromAPI, pageSize } from "../../../../../config"; +import { useTabItemPressWhenScreenActive } from "../../../../../hooks/useTabItemPressWhenScreenActive"; +import I18n from "../../../../../i18n"; +import { Dispatch } from "../../../../../store/actions/types"; +import { GlobalState } from "../../../../../store/reducers/types"; import customVariables, { VIBRATION_LONG_PRESS_DURATION -} from "../../../../theme/variables"; -import { useOnFirstRender } from "../../../../utils/hooks/useOnFirstRender"; +} from "../../../../../theme/variables"; +import { useOnFirstRender } from "../../../../../utils/hooks/useOnFirstRender"; import { Filter, loadNextPageMessages, loadPreviousPageMessages, reloadAllMessages -} from "../../store/actions"; +} from "../../../store/actions"; import { allArchiveSelector, allInboxSelector, @@ -38,8 +38,8 @@ import { isLoadingInboxPreviousPage, isReloadingArchive, isReloadingInbox -} from "../../store/reducers/allPaginated"; -import { UIMessage } from "../../types"; +} from "../../../store/reducers/allPaginated"; +import { UIMessage } from "../../../types"; import MessageListItem from "./Item"; import { EmptyComponent, diff --git a/ts/features/messages/components/__test__/EmptyListComponent.test.tsx b/ts/features/messages/components/__test__/EmptyListComponent.test.tsx index 4306177d2c2..dda642b7831 100644 --- a/ts/features/messages/components/__test__/EmptyListComponent.test.tsx +++ b/ts/features/messages/components/__test__/EmptyListComponent.test.tsx @@ -2,7 +2,7 @@ import { render } from "@testing-library/react-native"; import React from "react"; import I18n from "../../../../i18n"; -import { EmptyListComponent } from "../EmptyListComponent"; +import { EmptyListComponent } from "../Home/legacy/EmptyListComponent"; describe("EmptyListComponent", () => { it("matches the snapshot", () => { diff --git a/ts/features/messages/components/__test__/ListSelectionBar.test.tsx b/ts/features/messages/components/__test__/ListSelectionBar.test.tsx index 823d10f4bdf..1a231ddfcdf 100644 --- a/ts/features/messages/components/__test__/ListSelectionBar.test.tsx +++ b/ts/features/messages/components/__test__/ListSelectionBar.test.tsx @@ -2,7 +2,7 @@ import React from "react"; import { fireEvent, render } from "@testing-library/react-native"; import I18n from "../../../../i18n"; -import ListSelectionBar from "../ListSelectionBar"; +import ListSelectionBar from "../Home/legacy/ListSelectionBar"; jest.useFakeTimers(); diff --git a/ts/features/messages/components/__test__/MessageList.test.tsx b/ts/features/messages/components/__test__/MessageList.test.tsx index 16845931ae0..733d779901a 100644 --- a/ts/features/messages/components/__test__/MessageList.test.tsx +++ b/ts/features/messages/components/__test__/MessageList.test.tsx @@ -13,7 +13,7 @@ import { defaultRequestPayload, successReloadMessagesPayload } from "../../__mocks__/messages"; -import MessageList from "../MessageList"; +import MessageList from "../Home/legacy"; import { MESSAGES_ROUTES } from "../../navigation/routes"; jest.useFakeTimers(); diff --git a/ts/features/messages/components/__test__/MessagesInbox.test.tsx b/ts/features/messages/components/__test__/MessagesInbox.test.tsx index 0c216071fe6..43d99b55c12 100644 --- a/ts/features/messages/components/__test__/MessagesInbox.test.tsx +++ b/ts/features/messages/components/__test__/MessagesInbox.test.tsx @@ -10,7 +10,7 @@ import { GlobalState } from "../../../../store/reducers/types"; import { renderScreenWithNavigationStoreContext } from "../../../../utils/testWrapper"; import { successReloadMessagesPayload } from "../../__mocks__/messages"; -import MessagesInbox from "../MessagesInbox"; +import MessagesInbox from "../Home/legacy/MessagesInbox"; import { MESSAGES_ROUTES } from "../../navigation/routes"; jest.useFakeTimers(); diff --git a/ts/features/messages/components/__test__/MessagesSearch.test.tsx b/ts/features/messages/components/__test__/MessagesSearch.test.tsx index 0c66ae37a5f..56e60abc743 100644 --- a/ts/features/messages/components/__test__/MessagesSearch.test.tsx +++ b/ts/features/messages/components/__test__/MessagesSearch.test.tsx @@ -7,7 +7,7 @@ import { service_2, successReloadMessagesPayload } from "../../__mocks__/messages"; -import MessagesSearch from "../MessagesSearch"; +import MessagesSearch from "../Home/legacy/MessagesSearch"; const messages = successReloadMessagesPayload.messages; diff --git a/ts/features/messages/hooks/useMessageOpening.tsx b/ts/features/messages/hooks/useMessageOpening.tsx index dfc2c6aa6f3..66ab9d33549 100644 --- a/ts/features/messages/hooks/useMessageOpening.tsx +++ b/ts/features/messages/hooks/useMessageOpening.tsx @@ -19,7 +19,7 @@ import { isPnSupportedSelector, pnMinAppVersionSelector } from "../../../store/reducers/backendStatus"; -import { MessageFeedback } from "../components/MessageFeedback"; +import { MessageFeedback } from "../components/Home/legacy/MessageFeedback"; import { openAppStoreUrl } from "../../../utils/url"; import { PreconditionHeader, diff --git a/ts/features/messages/navigation/MessagesHomeTabNavigator.tsx b/ts/features/messages/navigation/MessagesHomeTabNavigator.tsx index eedf1bd910f..a93e291306c 100644 --- a/ts/features/messages/navigation/MessagesHomeTabNavigator.tsx +++ b/ts/features/messages/navigation/MessagesHomeTabNavigator.tsx @@ -6,7 +6,7 @@ import I18n from "../../../i18n"; import { makeFontStyleObject } from "../../../components/core/fonts"; import MessageListScreen, { MessagesHomeTabNavigationParams -} from "../screens/MessageListScreen"; +} from "../screens/legacy/MessageListScreen"; export const MessagesHomeTabRoutes = { MESSAGES_INBOX: "MESSAGES_INBOX", diff --git a/ts/features/messages/navigation/MessagesNavigator.tsx b/ts/features/messages/navigation/MessagesNavigator.tsx index 5c8932e57d1..40f450e93ea 100644 --- a/ts/features/messages/navigation/MessagesNavigator.tsx +++ b/ts/features/messages/navigation/MessagesNavigator.tsx @@ -2,7 +2,7 @@ import React from "react"; import { createStackNavigator } from "@react-navigation/stack"; import { EUCovidCertStackNavigator } from "../../euCovidCert/navigation/navigator"; import EUCOVIDCERT_ROUTES from "../../euCovidCert/navigation/routes"; -import LegacyMessageDetailScreen from "../screens/LegacyMessageDetailScreen"; +import LegacyMessageDetailScreen from "../screens/legacy/LegacyMessageDetailScreen"; import { MessageDetailsScreen } from "../screens/MessageDetailsScreen"; import { MessageCalendarScreen } from "../screens/MessageCalendarScreen"; import { MessageRouterScreen } from "../screens/MessageRouterScreen"; @@ -11,7 +11,7 @@ import PN_ROUTES from "../../pn/navigation/routes"; import { useIOSelector } from "../../../store/hooks"; import { isGestureEnabled } from "../../../utils/navigation"; import { isPnEnabledSelector } from "../../../store/reducers/backendStatus"; -import { LegacyMessageDetailAttachment } from "../screens/LegacyMessageAttachment"; +import { LegacyMessageDetailAttachment } from "../screens/legacy/LegacyMessageAttachment"; import { isDesignSystemEnabledSelector } from "../../../store/reducers/persistedPreferences"; import { MessageAttachmentScreen } from "../screens/MessageAttachmentScreen"; import { MessagesParamsList } from "./params"; diff --git a/ts/features/messages/screens/MessagesHomeScreen.tsx b/ts/features/messages/screens/MessagesHomeScreen.tsx index afe8cc4af52..b13d755127a 100644 --- a/ts/features/messages/screens/MessagesHomeScreen.tsx +++ b/ts/features/messages/screens/MessagesHomeScreen.tsx @@ -1,16 +1,16 @@ -import React from "react"; -import { StyleSheet, Text, View } from "react-native"; +import React, { useRef } from "react"; +import { View } from "react-native"; +import PagerView from "react-native-pager-view"; +import { IOStyles } from "@pagopa/io-app-design-system"; +import { PagerViewContainer } from "../components/Home/PagerViewContainer"; +import { TabNavigationContainer } from "../components/Home/TabNavigationContainer"; -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: "center", - justifyContent: "center" - } -}); - -export const MessagesHomeScreen = () => ( - - {"Questa sezione è in fase di sviluppo"} - -); +export const MessagesHomeScreen = () => { + const pagerViewRef = useRef(null); + return ( + + + + + ); +}; diff --git a/ts/features/messages/screens/__tests__/LegacyMessagesHomeScreen.test.tsx b/ts/features/messages/screens/__tests__/LegacyMessagesHomeScreen.test.tsx index 6b2ecb87844..545595867e8 100644 --- a/ts/features/messages/screens/__tests__/LegacyMessagesHomeScreen.test.tsx +++ b/ts/features/messages/screens/__tests__/LegacyMessagesHomeScreen.test.tsx @@ -17,7 +17,7 @@ import { } from "../../../../store/reducers/__mock__/backendStatus"; import { renderScreenWithNavigationStoreContext } from "../../../../utils/testWrapper"; import { successReloadMessagesPayload } from "../../__mocks__/messages"; -import LegacyMessagesHomeScreen from "../LegacyMessagesHomeScreen"; +import LegacyMessagesHomeScreen from "../legacy/LegacyMessagesHomeScreen"; import { MESSAGES_ROUTES } from "../../navigation/routes"; const mockNavigate = jest.fn(); @@ -133,7 +133,8 @@ const renderComponent = ( }), lastRequest: O.none }, - migration: O.none + migration: O.none, + shownCategory: "INBOX" } as AllPaginated; const mockStore = configureMockStore(); diff --git a/ts/features/messages/screens/LegacyMessageAttachment.tsx b/ts/features/messages/screens/legacy/LegacyMessageAttachment.tsx similarity index 81% rename from ts/features/messages/screens/LegacyMessageAttachment.tsx rename to ts/features/messages/screens/legacy/LegacyMessageAttachment.tsx index c19ec6e841b..9f37a0d3765 100644 --- a/ts/features/messages/screens/LegacyMessageAttachment.tsx +++ b/ts/features/messages/screens/legacy/LegacyMessageAttachment.tsx @@ -2,18 +2,18 @@ import { IOToast } from "@pagopa/io-app-design-system"; import { useNavigation } from "@react-navigation/native"; import * as O from "fp-ts/lib/Option"; import React, { useEffect, useRef } from "react"; -import I18n from "../../../i18n"; -import { IOStackNavigationRouteProps } from "../../../navigation/params/AppParamsList"; -import { useIOSelector } from "../../../store/hooks"; +import I18n from "../../../../i18n"; +import { IOStackNavigationRouteProps } from "../../../../navigation/params/AppParamsList"; +import { useIOSelector } from "../../../../store/hooks"; import { trackThirdPartyMessageAttachmentCorruptedFile, trackThirdPartyMessageAttachmentPreviewSuccess, trackThirdPartyMessageAttachmentUserAction -} from "../analytics"; -import { LegacyMessageAttachmentPreview } from "../components/MessageAttachment/LegacyMessageAttachmentPreview"; -import { MessagesParamsList } from "../navigation/params"; -import { getServiceByMessageId } from "../store/reducers/paginatedById"; -import { thirdPartyMessageAttachment } from "../store/reducers/thirdPartyById"; +} from "../../analytics"; +import { LegacyMessageAttachmentPreview } from "../../components/MessageAttachment/LegacyMessageAttachmentPreview"; +import { MessagesParamsList } from "../../navigation/params"; +import { getServiceByMessageId } from "../../store/reducers/paginatedById"; +import { thirdPartyMessageAttachment } from "../../store/reducers/thirdPartyById"; export const LegacyMessageDetailAttachment = ( props: IOStackNavigationRouteProps< diff --git a/ts/features/messages/screens/LegacyMessageDetailScreen.tsx b/ts/features/messages/screens/legacy/LegacyMessageDetailScreen.tsx similarity index 77% rename from ts/features/messages/screens/LegacyMessageDetailScreen.tsx rename to ts/features/messages/screens/legacy/LegacyMessageDetailScreen.tsx index a745b538850..4aab6057158 100644 --- a/ts/features/messages/screens/LegacyMessageDetailScreen.tsx +++ b/ts/features/messages/screens/legacy/LegacyMessageDetailScreen.tsx @@ -10,36 +10,36 @@ import { } from "react-native"; import { VSpacer } from "@pagopa/io-app-design-system"; import { Route, useRoute } from "@react-navigation/native"; -import { ServiceId } from "../../../../definitions/backend/ServiceId"; -import { Body } from "../../../components/core/typography/Body"; -import { IOStyles } from "../../../components/core/variables/IOStyles"; -import WorkunitGenericFailure from "../../../components/error/WorkunitGenericFailure"; -import MessageDetailComponent from "../components/MessageDetail"; -import ErrorState from "../components/MessageDetail/ErrorState"; +import { ServiceId } from "../../../../../definitions/backend/ServiceId"; +import { Body } from "../../../../components/core/typography/Body"; +import { IOStyles } from "../../../../components/core/variables/IOStyles"; +import WorkunitGenericFailure from "../../../../components/error/WorkunitGenericFailure"; +import MessageDetailComponent from "../../components/MessageDetail"; +import ErrorState from "../../components/MessageDetail/ErrorState"; import BaseScreenComponent, { ContextualHelpPropsMarkdown -} from "../../../components/screens/BaseScreenComponent"; -import I18n from "../../../i18n"; -import { isNoticePaidSelector } from "../../../store/reducers/entities/payments"; +} from "../../../../components/screens/BaseScreenComponent"; +import I18n from "../../../../i18n"; +import { isNoticePaidSelector } from "../../../../store/reducers/entities/payments"; import { loadMessageDetails, resetGetMessageDataAction -} from "../store/actions"; +} from "../../store/actions"; import { navigateBack, navigateToServiceDetailsScreen -} from "../../../store/actions/navigation"; -import { loadServiceDetail } from "../../services/details/store/actions/details"; -import { messageDetailsByIdSelector } from "../store/reducers/detailsById"; -import { getPaginatedMessageById } from "../store/reducers/paginatedById"; -import { UIMessageId } from "../types"; +} from "../../../../store/actions/navigation"; +import { loadServiceDetail } from "../../../services/details/store/actions/details"; +import { messageDetailsByIdSelector } from "../../store/reducers/detailsById"; +import { getPaginatedMessageById } from "../../store/reducers/paginatedById"; +import { UIMessageId } from "../../types"; import { serviceByIdPotSelector, serviceMetadataByIdSelector -} from "../../services/details/store/reducers/servicesById"; -import { toUIService } from "../../../store/reducers/entities/services/transformers"; -import { useOnFirstRender } from "../../../utils/hooks/useOnFirstRender"; -import { useIODispatch, useIOSelector } from "../../../store/hooks"; +} from "../../../services/details/store/reducers/servicesById"; +import { toUIService } from "../../../../store/reducers/entities/services/transformers"; +import { useOnFirstRender } from "../../../../utils/hooks/useOnFirstRender"; +import { useIODispatch, useIOSelector } from "../../../../store/hooks"; const styles = StyleSheet.create({ notFullStateContainer: { diff --git a/ts/features/messages/screens/LegacyMessagesHomeScreen.tsx b/ts/features/messages/screens/legacy/LegacyMessagesHomeScreen.tsx similarity index 79% rename from ts/features/messages/screens/LegacyMessagesHomeScreen.tsx rename to ts/features/messages/screens/legacy/LegacyMessagesHomeScreen.tsx index 2e870ccd866..a13f15e7640 100644 --- a/ts/features/messages/screens/LegacyMessagesHomeScreen.tsx +++ b/ts/features/messages/screens/legacy/LegacyMessagesHomeScreen.tsx @@ -6,41 +6,41 @@ import { pipe } from "fp-ts/lib/function"; import React, { useEffect } from "react"; import { connect } from "react-redux"; import { Dispatch } from "redux"; -import SectionStatusComponent from "../../../components/SectionStatus"; -import { ContextualHelpPropsMarkdown } from "../../../components/screens/BaseScreenComponent"; -import TopScreenComponent from "../../../components/screens/TopScreenComponent"; -import { MIN_CHARACTER_SEARCH_TEXT } from "../../../components/search/SearchButton"; -import { SearchNoResultMessage } from "../../../components/search/SearchNoResultMessage"; -import FocusAwareStatusBar from "../../../components/ui/FocusAwareStatusBar"; -import { useSecuritySuggestionsBottomSheet } from "../../../hooks/useSecuritySuggestionBottomSheet"; -import I18n from "../../../i18n"; -import { sectionStatusSelector } from "../../../store/reducers/backendStatus"; +import SectionStatusComponent from "../../../../components/SectionStatus"; +import { ContextualHelpPropsMarkdown } from "../../../../components/screens/BaseScreenComponent"; +import TopScreenComponent from "../../../../components/screens/TopScreenComponent"; +import { MIN_CHARACTER_SEARCH_TEXT } from "../../../../components/search/SearchButton"; +import { SearchNoResultMessage } from "../../../../components/search/SearchNoResultMessage"; +import FocusAwareStatusBar from "../../../../components/ui/FocusAwareStatusBar"; +import { useSecuritySuggestionsBottomSheet } from "../../../../hooks/useSecuritySuggestionBottomSheet"; +import I18n from "../../../../i18n"; +import { sectionStatusSelector } from "../../../../store/reducers/backendStatus"; import { isSearchMessagesEnabledSelector, searchTextSelector -} from "../../../store/reducers/search"; -import { GlobalState } from "../../../store/reducers/types"; +} from "../../../../store/reducers/search"; +import { GlobalState } from "../../../../store/reducers/types"; import { setAccessibilityFocus, useScreenReaderEnabled -} from "../../../utils/accessibility"; -import { useOnFirstRender } from "../../../utils/hooks/useOnFirstRender"; -import MessageList from "../components/MessageList"; -import MessagesSearch from "../components/MessagesSearch"; -import { useMessageOpening } from "../hooks/useMessageOpening"; -import MessagesHomeTabNavigator from "../navigation/MessagesHomeTabNavigator"; +} from "../../../../utils/accessibility"; +import { useOnFirstRender } from "../../../../utils/hooks/useOnFirstRender"; +import MessageList from "../../components/Home/legacy"; +import MessagesSearch from "../../components/Home/legacy/MessagesSearch"; +import { useMessageOpening } from "../../hooks/useMessageOpening"; +import MessagesHomeTabNavigator from "../../navigation/MessagesHomeTabNavigator"; import { migrateToPaginatedMessages, resetMigrationStatus -} from "../store/actions"; +} from "../../store/actions"; import { allInboxAndArchivedMessagesSelector, allPaginatedSelector -} from "../store/reducers/allPaginated"; +} from "../../store/reducers/allPaginated"; import { MessagesStatus, messagesStatusSelector -} from "../store/reducers/messagesStatus"; +} from "../../store/reducers/messagesStatus"; import MigratingMessage from "./MigratingMessage"; type Props = ReturnType & diff --git a/ts/features/messages/screens/MessageListScreen.tsx b/ts/features/messages/screens/legacy/MessageListScreen.tsx similarity index 78% rename from ts/features/messages/screens/MessageListScreen.tsx rename to ts/features/messages/screens/legacy/MessageListScreen.tsx index e6ceaa7308e..1e9eeecc312 100644 --- a/ts/features/messages/screens/MessageListScreen.tsx +++ b/ts/features/messages/screens/legacy/MessageListScreen.tsx @@ -3,20 +3,20 @@ import { RouteProp, useRoute } from "@react-navigation/native"; 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 { useMessageOpening } from "../hooks/useMessageOpening"; -import MessagesInbox from "../components/MessagesInbox"; -import { upsertMessageStatusAttributes } from "../store/actions"; -import { useIODispatch, useIOSelector } from "../../../store/hooks"; +import { useMessageOpening } from "../../hooks/useMessageOpening"; +import MessagesInbox from "../../components/Home/legacy/MessagesInbox"; +import { upsertMessageStatusAttributes } from "../../store/actions"; +import { useIODispatch, useIOSelector } from "../../../../store/hooks"; import { MessagePagePot, messagesByCategorySelector -} from "../store/reducers/allPaginated"; -import { UIMessage } from "../types"; -import { MessagesHomeTabParamsList } from "../navigation/MessagesHomeTabNavigator"; -import MessagesArchive from "../components/MessagesArchive"; +} from "../../store/reducers/allPaginated"; +import { UIMessage } from "../../types"; +import { MessagesHomeTabParamsList } from "../../navigation/MessagesHomeTabNavigator"; +import MessagesArchive from "../../components/Home/legacy/MessagesArchive"; import messageListData, { MessageListCategory -} from "../types/messageListCategory"; +} from "../../types/messageListCategory"; export type MessagesHomeTabNavigationParams = Readonly<{ category: MessageListCategory; diff --git a/ts/features/messages/screens/MigratingMessage.tsx b/ts/features/messages/screens/legacy/MigratingMessage.tsx similarity index 88% rename from ts/features/messages/screens/MigratingMessage.tsx rename to ts/features/messages/screens/legacy/MigratingMessage.tsx index 9eb38baad2a..f28d3f1377a 100644 --- a/ts/features/messages/screens/MigratingMessage.tsx +++ b/ts/features/messages/screens/legacy/MigratingMessage.tsx @@ -9,13 +9,13 @@ import { Text, View } from "react-native"; -import doubtImage from "../../../../img/pictograms/doubt.png"; -import paymentCompletedImage from "../../../../img/pictograms/payment-completed.png"; -import { H2 } from "../../../components/core/typography/H2"; -import { renderInfoRasterImage } from "../../../components/infoScreen/imageRendering"; -import I18n from "../../../i18n"; -import customVariables from "../../../theme/variables"; -import { MigrationStatus } from "../store/reducers/allPaginated"; +import doubtImage from "../../../../../img/pictograms/doubt.png"; +import paymentCompletedImage from "../../../../../img/pictograms/payment-completed.png"; +import { H2 } from "../../../../components/core/typography/H2"; +import { renderInfoRasterImage } from "../../../../components/infoScreen/imageRendering"; +import I18n from "../../../../i18n"; +import customVariables from "../../../../theme/variables"; +import { MigrationStatus } from "../../store/reducers/allPaginated"; const styles = StyleSheet.create({ migrationMessageContainer: { diff --git a/ts/features/messages/store/actions/index.ts b/ts/features/messages/store/actions/index.ts index a7f52ecb680..3d98adeb7c3 100644 --- a/ts/features/messages/store/actions/index.ts +++ b/ts/features/messages/store/actions/index.ts @@ -19,6 +19,7 @@ import { MessagesStatus } from "../reducers/messagesStatus"; import { ThirdPartyAttachment } from "../../../../../definitions/backend/ThirdPartyAttachment"; import { PaymentRequestsGetResponse } from "../../../../../definitions/backend/PaymentRequestsGetResponse"; import { Detail_v2Enum } from "../../../../../definitions/backend/PaymentProblemJson"; +import { MessageListCategory } from "../../types/messageListCategory"; export type ThirdPartyMessageActions = ActionType; @@ -313,6 +314,10 @@ export const addUserSelectedPaymentRptId = createAction( resolve => (paymentId: string) => resolve({ paymentId }) ); +export const setShownMessageCategoryAction = createStandardAction( + "SET_SHOWN_MESSAGE_CATEGORY" +)(); + export type MessagesActions = ActionType< | typeof reloadAllMessages | typeof loadNextPageMessages @@ -336,4 +341,5 @@ export type MessagesActions = ActionType< | typeof updatePaymentForMessage | typeof cancelQueuedPaymentUpdates | typeof addUserSelectedPaymentRptId + | typeof setShownMessageCategoryAction >; diff --git a/ts/features/messages/store/reducers/__tests__/allPaginated.test.ts b/ts/features/messages/store/reducers/__tests__/allPaginated.test.ts index e787be8bf7b..21fd7898e17 100644 --- a/ts/features/messages/store/reducers/__tests__/allPaginated.test.ts +++ b/ts/features/messages/store/reducers/__tests__/allPaginated.test.ts @@ -12,7 +12,10 @@ import { import { loadNextPageMessages, loadPreviousPageMessages, + migrateToPaginatedMessages, reloadAllMessages, + resetMigrationStatus, + setShownMessageCategoryAction, upsertMessageStatusAttributes, UpsertMessageStatusAttributesPayload } from "../../actions"; @@ -23,8 +26,14 @@ import reducer, { isLoadingInboxPreviousPage, AllPaginated, isLoadingInboxNextPage, - isLoadingOrUpdatingInbox + isLoadingOrUpdatingInbox, + shownMessageCategorySelector } from "../allPaginated"; +import { pageSize } from "../../../../../config"; +import { UIMessage } from "../../../types"; +import { clearCache } from "../../../../../store/actions/profile"; +import { appReducer } from "../../../../../store/reducers"; +import { applicationChangeState } from "../../../../../store/actions/application"; describe("allPaginated reducer", () => { describe("given a `reloadAllMessages` action", () => { @@ -480,6 +489,232 @@ describe("allPaginated reducer", () => { }); }); + describe("when `setShownMessageCategoryAction` is received", () => { + it("should change from INBOX to ARCHIVE", () => { + const allPaginatedInitialState = reducer( + undefined, + setShownMessageCategoryAction("INBOX") + ); + expect(allPaginatedInitialState.shownCategory).toBe("INBOX"); + const allPaginatedFinalState = reducer( + allPaginatedInitialState, + setShownMessageCategoryAction("ARCHIVE") + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + it("should change from ARCHIVE to INBOX", () => { + const allPaginatedInitialState = reducer( + undefined, + setShownMessageCategoryAction("ARCHIVE") + ); + expect(allPaginatedInitialState.shownCategory).toBe("ARCHIVE"); + const allPaginatedFinalState = reducer( + allPaginatedInitialState, + setShownMessageCategoryAction("INBOX") + ); + expect(allPaginatedFinalState.shownCategory).toBe("INBOX"); + }); + it("should stay ARCHIVE", () => { + const allPaginatedInitialState = reducer( + undefined, + setShownMessageCategoryAction("ARCHIVE") + ); + expect(allPaginatedInitialState.shownCategory).toBe("ARCHIVE"); + const allPaginatedFinalState = reducer( + allPaginatedInitialState, + setShownMessageCategoryAction("ARCHIVE") + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + it("should stay INBOX", () => { + const allPaginatedInitialState = reducer( + undefined, + setShownMessageCategoryAction("INBOX") + ); + expect(allPaginatedInitialState.shownCategory).toBe("INBOX"); + const allPaginatedFinalState = reducer( + allPaginatedInitialState, + setShownMessageCategoryAction("INBOX") + ); + expect(allPaginatedFinalState.shownCategory).toBe("INBOX"); + }); + }); + + describe("when an action that is not `setShownMessageCategoryAction` is received", () => { + const allPaginatedInitialState = () => { + const allPaginatedInitialState = reducer( + undefined, + setShownMessageCategoryAction("ARCHIVE") + ); + expect(allPaginatedInitialState.shownCategory).toBe("ARCHIVE"); + return allPaginatedInitialState; + }; + it("should keep its `showCategory` value (reloadAllMessages.request)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + reloadAllMessages.request({ pageSize, filter: { getArchived: true } }) + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + it("should keep its `showCategory` value (reloadAllMessages.success)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + reloadAllMessages.success({ + messages: [], + filter: { getArchived: true }, + pagination: {} + }) + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + it("should keep its `showCategory` value (reloadAllMessages.failure)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + reloadAllMessages.failure({ + error: new Error(""), + filter: { getArchived: true } + }) + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + + it("should keep its `showCategory` value (loadNextPageMessages.request)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + loadNextPageMessages.request({ + pageSize, + filter: { getArchived: true } + }) + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + it("should keep its `showCategory` value (loadNextPageMessages.success)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + loadNextPageMessages.success({ + messages: [], + filter: { getArchived: true }, + pagination: {} + }) + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + it("should keep its `showCategory` value (loadNextPageMessages.failure)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + loadNextPageMessages.failure({ + error: new Error(""), + filter: { getArchived: true } + }) + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + + it("should keep its `showCategory` value (loadPreviousPageMessages.request)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + loadPreviousPageMessages.request({ + pageSize, + filter: { getArchived: true } + }) + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + it("should keep its `showCategory` value (loadPreviousPageMessages.success)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + loadPreviousPageMessages.success({ + messages: [], + filter: { getArchived: true }, + pagination: {} + }) + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + it("should keep its `showCategory` value (loadPreviousPageMessages.failure)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + loadPreviousPageMessages.failure({ + error: new Error(""), + filter: { getArchived: true } + }) + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + + it("should keep its `showCategory` value (upsertMessageStatusAttributes.request)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + upsertMessageStatusAttributes.request({ + message: { isRead: true } as UIMessage, + update: { isArchived: false, tag: "bulk" } + }) + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + it("should keep its `showCategory` value (upsertMessageStatusAttributes.success)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + upsertMessageStatusAttributes.success({ + message: { isRead: true } as UIMessage, + update: { isArchived: false, tag: "bulk" } + }) + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + it("should keep its `showCategory` value (upsertMessageStatusAttributes.failure)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + upsertMessageStatusAttributes.failure({ + error: new Error(""), + payload: { + message: { isRead: true } as UIMessage, + update: { isArchived: false, tag: "bulk" } + } + }) + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + + it("should keep its `showCategory` value (migrateToPaginatedMessages.request)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + migrateToPaginatedMessages.request({}) + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + it("should keep its `showCategory` value (migrateToPaginatedMessages.success)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + migrateToPaginatedMessages.success(0) + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + it("should keep its `showCategory` value (migrateToPaginatedMessages.failure)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + migrateToPaginatedMessages.failure({ failed: [], succeeded: [] }) + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + + it("should keep its `showCategory` value (resetMigrationStatus)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + resetMigrationStatus() + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + + it("should keep its `showCategory` value (clearCache)", () => { + const allPaginatedFinalState = reducer( + allPaginatedInitialState(), + clearCache() + ); + expect(allPaginatedFinalState.shownCategory).toBe("ARCHIVE"); + }); + }); + [ [ reloadAllMessages.request(defaultRequestPayload), @@ -518,7 +753,8 @@ describe("allPaginated reducer", () => { const defaultState: AllPaginated = { inbox: { data: pot.none, lastRequest: O.none }, archive: { data: pot.none, lastRequest: O.none }, - migration: O.none + migration: O.none, + shownCategory: "INBOX" }; function toGlobalState(localState: AllPaginated): GlobalState { @@ -1105,3 +1341,27 @@ describe("Message state upsert", () => { }); }); }); + +describe("shownMessageCategorySelector", () => { + it("should return INBOX for the initial state", () => { + const globalState = appReducer(undefined, applicationChangeState("active")); + const shownCategory = shownMessageCategorySelector(globalState); + expect(shownCategory).toBe("INBOX"); + }); + it("should return INBOX when shownCategory is INBOX", () => { + const globalState = appReducer( + undefined, + setShownMessageCategoryAction("INBOX") + ); + const shownCategory = shownMessageCategorySelector(globalState); + expect(shownCategory).toBe("INBOX"); + }); + it("should return ARCHIVE when shownCategory is ARCHIVE", () => { + const globalState = appReducer( + undefined, + setShownMessageCategoryAction("ARCHIVE") + ); + const shownCategory = shownMessageCategorySelector(globalState); + expect(shownCategory).toBe("ARCHIVE"); + }); +}); diff --git a/ts/features/messages/store/reducers/allPaginated.ts b/ts/features/messages/store/reducers/allPaginated.ts index ed2bda50c8e..fed58ce7f77 100644 --- a/ts/features/messages/store/reducers/allPaginated.ts +++ b/ts/features/messages/store/reducers/allPaginated.ts @@ -14,6 +14,7 @@ import { MigrationResult, reloadAllMessages, resetMigrationStatus, + setShownMessageCategoryAction, upsertMessageStatusAttributes } from "../actions"; import { clearCache } from "../../../../store/actions/profile"; @@ -21,14 +22,13 @@ import { Action } from "../../../../store/actions/types"; import { GlobalState } from "../../../../store/reducers/types"; import { UIMessage } from "../../types"; -export type MessagePagePot = pot.Pot< - { - page: ReadonlyArray; - previous?: string; - next?: string; - }, - string ->; +export type MessagePage = { + page: ReadonlyArray; + previous?: string; + next?: string; +}; + +export type MessagePagePot = pot.Pot; type Collection = { data: MessagePagePot; @@ -44,27 +44,29 @@ export type MigrationStatus = O.Option< export type MessageOperation = "archive" | "restore"; export type MessageOperationFailure = { - operation: MessageOperation; error: Error; + operation: MessageOperation; }; /** * A list of messages and pagination inbox. */ export type AllPaginated = { - inbox: Collection; archive: Collection; - migration: MigrationStatus; + inbox: Collection; latestMessageOperation?: Either.Either< MessageOperationFailure, MessageOperation >; + migration: MigrationStatus; + shownCategory: MessageListCategory; }; const INITIAL_STATE: AllPaginated = { - inbox: { data: pot.none, lastRequest: O.none }, archive: { data: pot.none, lastRequest: O.none }, - migration: O.none + inbox: { data: pot.none, lastRequest: O.none }, + migration: O.none, + shownCategory: "INBOX" }; /** @@ -75,6 +77,11 @@ const reducer = ( action: Action ): AllPaginated => { switch (action.type) { + case getType(setShownMessageCategoryAction): + return { + ...state, + shownCategory: action.payload + }; case getType(reloadAllMessages.request): case getType(reloadAllMessages.success): case getType(reloadAllMessages.failure): @@ -124,7 +131,10 @@ const reducer = ( /* END Migration-related block */ case getType(clearCache): - return INITIAL_STATE; + return { + ...INITIAL_STATE, + shownCategory: state.shownCategory + }; default: return state; @@ -712,4 +722,7 @@ export const getCursors = createSelector( }) ); +export const shownMessageCategorySelector = (state: GlobalState) => + state.entities.messages.allPaginated.shownCategory; + export default reducer; diff --git a/ts/navigation/TabNavigator.tsx b/ts/navigation/TabNavigator.tsx index 3718b555746..3306a6cd159 100644 --- a/ts/navigation/TabNavigator.tsx +++ b/ts/navigation/TabNavigator.tsx @@ -6,7 +6,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context"; import LoadingSpinnerOverlay from "../components/LoadingSpinnerOverlay"; import { makeFontStyleObject } from "../components/core/fonts"; import { TabIconComponent } from "../components/ui/TabIconComponent"; -import LegacyMessagesHomeScreen from "../features/messages/screens/LegacyMessagesHomeScreen"; +import LegacyMessagesHomeScreen from "../features/messages/screens/legacy/LegacyMessagesHomeScreen"; import { MessagesHomeScreen } from "../features/messages/screens/MessagesHomeScreen"; import { WalletHomeScreen as NewWalletHomeScreen } from "../features/newWallet/screens/WalletHomeScreen"; import { PaymentsHomeScreen } from "../features/payments/home/screens/PaymentsHomeScreen";