Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 14 additions & 37 deletions src/pages/inbox/report/ReportActionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@ import type {LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent} from 'r
import {DeviceEventEmitter, InteractionManager, View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import {renderScrollComponent as renderActionSheetAwareScrollView} from '@components/ActionSheetAwareScrollView';
import Button from '@components/Button';
import InvertedFlashList from '@components/FlashList/InvertedFlashList';
import getShowScrollIndicator from '@components/FlashList/InvertedFlashList/getShowScrollIndicator';
import {usePersonalDetails} from '@components/OnyxListItemProvider';
import ReportActionsSkeletonView from '@components/ReportActionsSkeletonView';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useIsAnonymousUser from '@hooks/useIsAnonymousUser';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useNetworkWithOfflineStatus from '@hooks/useNetworkWithOfflineStatus';
import useOnyx from '@hooks/useOnyx';
Expand Down Expand Up @@ -62,7 +60,6 @@ import {
} from '@libs/ReportUtils';
import Visibility from '@libs/Visibility';
import type {ReportsSplitNavigatorParamList} from '@navigation/types';
import ConciergeThinkingMessage from '@pages/home/report/ConciergeThinkingMessage';
import {ActionListContext} from '@pages/inbox/ReportScreenContext';
import variables from '@styles/variables';
import {openReport, readNewestAction, subscribeToNewActionEvent} from '@userActions/Report';
Expand All @@ -74,9 +71,10 @@ import type * as OnyxTypes from '@src/types/onyx';
import FloatingMessageCounter from './FloatingMessageCounter';
import getInitialNumToRender from './getInitialNumReportActionsToRender';
import getReportActionsListInitialNumToRender from './getReportActionsListInitialNumToRender';
import ListBoundaryLoader from './ListBoundaryLoader';
import ReportActionsListHeader from './ReportActionsListHeader';
import ReportActionsListItemRenderer from './ReportActionsListItemRenderer';
import {getUnreadMarkerReportAction} from './shouldDisplayNewMarkerOnReportAction';
import ShowPreviousMessagesButton from './ShowPreviousMessagesButton';
import StaticReportActionsPreview from './StaticReportActionsPreview';
import useReportUnreadMessageScrollTracking from './useReportUnreadMessageScrollTracking';

Expand Down Expand Up @@ -120,9 +118,6 @@ type ReportActionsListProps = {
/** Whether the optimistic CREATED report action was added */
hasCreatedActionAdded?: boolean;

/** Whether this is a Concierge chat in the side panel */
isConciergeSidePanel?: boolean;

/** Whether the chat history is hidden (concierge side panel fresh state) */
showHiddenHistory?: boolean;

Expand Down Expand Up @@ -166,7 +161,6 @@ function ReportActionsList({
listID,
parentReportActionForTransactionThread,
hasCreatedActionAdded,
isConciergeSidePanel,
showHiddenHistory,
hasPreviousMessages,
onShowPreviousMessages,
Expand All @@ -177,7 +171,6 @@ function ReportActionsList({
const styles = useThemeStyles();
const StyleUtils = useStyleUtils();
const {translate} = useLocalize();
const expensifyIcons = useMemoizedLazyExpensifyIcons(['UpArrow']);
const {windowHeight} = useWindowDimensions();
const {shouldUseNarrowLayout} = useResponsiveLayout();

Expand Down Expand Up @@ -701,7 +694,6 @@ function ReportActionsList({
const renderItem = useCallback(
({item: reportAction, index}: ListRenderItemInfo<OnyxTypes.ReportAction>) => {
const originalReportID = getOriginalReportID(report.reportID, reportAction, reportActionsFromOnyx);
const showPreviousMessagesButton = reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED && !!isConciergeSidePanel && !!showHiddenHistory && !!hasPreviousMessages;

// Use the action's actual index in sortedVisibleReportActions rather than the FlashList-provided index,
// because useFlashListScrollKey may slice the data for deep-link scroll positioning, making the
Expand Down Expand Up @@ -735,21 +727,13 @@ function ReportActionsList({
reportNameValuePairsOrigin={reportNameValuePairs?.origin}
reportNameValuePairsOriginalID={reportNameValuePairs?.originalID}
/>
{showPreviousMessagesButton && (
<View style={[styles.flexRow, styles.alignItemsCenter, styles.pv3, styles.mh5]}>
<View style={[styles.threadDividerLine, styles.ml0, styles.mr0, styles.flexGrow1]} />
<View>
<Button
small
shouldShowRightIcon
iconRight={expensifyIcons.UpArrow}
text={translate('common.concierge.showHistory')}
onPress={onShowPreviousMessages}
/>
</View>
<View style={[styles.threadDividerLine, styles.ml0, styles.mr0, styles.flexGrow1]} />
</View>
)}
<ShowPreviousMessagesButton
reportID={report.reportID}
actionType={reportAction.actionName}
hasPreviousMessages={!!hasPreviousMessages}
showFullHistory={!showHiddenHistory}
onPress={onShowPreviousMessages}
/>
</>
);
},
Expand All @@ -773,13 +757,9 @@ function ReportActionsList({
reportNameValuePairs?.origin,
reportNameValuePairs?.originalID,
reportActionsFromOnyx,
isConciergeSidePanel,
showHiddenHistory,
hasPreviousMessages,
onShowPreviousMessages,
styles,
translate,
expensifyIcons.UpArrow,
],
);

Expand Down Expand Up @@ -821,15 +801,12 @@ function ReportActionsList({
}

return (
<>
<ConciergeThinkingMessage report={report} />
<ListBoundaryLoader
type={CONST.LIST_COMPONENTS.HEADER}
onRetry={retryLoadNewerChatsError}
/>
</>
<ReportActionsListHeader
reportID={report.reportID}
onRetry={retryLoadNewerChatsError}
/>
);
}, [canShowHeader, report, retryLoadNewerChatsError]);
}, [canShowHeader, report.reportID, retryLoadNewerChatsError]);

const shouldShowSkeleton = isOffline && !sortedVisibleReportActions.some((action) => action.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED);

Expand Down
30 changes: 30 additions & 0 deletions src/pages/inbox/report/ReportActionsListHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import useOnyx from '@hooks/useOnyx';
import ConciergeThinkingMessage from '@pages/home/report/ConciergeThinkingMessage';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ListBoundaryLoader from './ListBoundaryLoader';

type ReportActionsListHeaderProps = {
/** The ID of the report being displayed */
reportID: string;

/** Callback to retry loading newer chats after an error */
onRetry: () => void;
};

function ReportActionsListHeader({reportID, onRetry}: ReportActionsListHeaderProps) {
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`);

return (
<>
<ConciergeThinkingMessage report={report} />
<ListBoundaryLoader
type={CONST.LIST_COMPONENTS.HEADER}
onRetry={onRetry}
/>
</>
);
}

export default ReportActionsListHeader;
1 change: 0 additions & 1 deletion src/pages/inbox/report/ReportActionsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,6 @@ function ReportActionsView({reportID, onLayout}: ReportActionsViewProps) {
loadNewerChats={loadNewerChats}
listID={listID}
hasCreatedActionAdded={shouldAddCreatedAction}
isConciergeSidePanel={isConciergeSidePanel}
showHiddenHistory={!showFullHistory}
hasPreviousMessages={hasPreviousMessages}
onShowPreviousMessages={handleShowPreviousMessages}
Expand Down
71 changes: 71 additions & 0 deletions src/pages/inbox/report/ShowPreviousMessagesButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from 'react';
import {View} from 'react-native';
import Button from '@components/Button';
import useIsInSidePanel from '@hooks/useIsInSidePanel';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {ReportAction} from '@src/types/onyx';

type ShowPreviousMessagesButtonProps = {
/** The ID of the report this list item belongs to */
reportID: string;

/** The action type of the report action being rendered for this list item */
actionType: ReportAction['actionName'];

/** Whether there are previous messages hidden before the session start */
hasPreviousMessages: boolean;

/** Whether the full message history is currently shown */
showFullHistory: boolean;

/** Callback to reveal the full message history */
onPress?: () => void;
};

function ShowPreviousMessagesButton({reportID, actionType, hasPreviousMessages, showFullHistory, onPress}: ShowPreviousMessagesButtonProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const expensifyIcons = useMemoizedLazyExpensifyIcons(['UpArrow']);
const isInSidePanel = useIsInSidePanel();
const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID);
const isConciergeSidePanel = isInSidePanel && reportID === conciergeReportID;

if (!isConciergeSidePanel) {
return null;
}
if (!onPress) {
return null;
}
if (actionType !== CONST.REPORT.ACTIONS.TYPE.CREATED) {
return null;
}
if (!hasPreviousMessages) {
return null;
}
if (showFullHistory) {
Comment thread
rlinoz marked this conversation as resolved.
return null;
}

return (
<View style={[styles.flexRow, styles.alignItemsCenter, styles.pv3, styles.mh5]}>
<View style={[styles.threadDividerLine, styles.ml0, styles.mr0, styles.flexGrow1]} />
<View>
<Button
small
shouldShowRightIcon
iconRight={expensifyIcons.UpArrow}
text={translate('common.concierge.showHistory')}
onPress={onPress}
/>
</View>
<View style={[styles.threadDividerLine, styles.ml0, styles.mr0, styles.flexGrow1]} />
</View>
);
}

export default ShowPreviousMessagesButton;
65 changes: 65 additions & 0 deletions tests/unit/ReportActionsListHeaderTest.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {render, screen} from '@testing-library/react-native';
import React from 'react';
import ReportActionsListHeader from '@pages/inbox/report/ReportActionsListHeader';

const REPORT_ID = '42';

const mockUseOnyx = jest.fn<unknown[], [string]>();

jest.mock('@hooks/useOnyx', () => ({
// eslint-disable-next-line @typescript-eslint/naming-convention
__esModule: true,
default: (key: string) => mockUseOnyx(key),
}));

jest.mock('@pages/home/report/ConciergeThinkingMessage', () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const {View: MockView} = require('react-native');
return {
// eslint-disable-next-line @typescript-eslint/naming-convention
__esModule: true,
default: () => <MockView testID="ConciergeThinkingMessage" />,
};
});

jest.mock('@pages/inbox/report/ListBoundaryLoader', () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const {View: MockView} = require('react-native');
return {
// eslint-disable-next-line @typescript-eslint/naming-convention
__esModule: true,
default: () => <MockView testID="ListBoundaryLoader" />,
};
});

describe('ReportActionsListHeader', () => {
beforeEach(() => {
jest.clearAllMocks();
mockUseOnyx.mockImplementation((key: string) => {
if (key.startsWith('report_')) {
return [{reportID: REPORT_ID}];
}
return [undefined];
});
});

it('renders ListBoundaryLoader', () => {
render(
<ReportActionsListHeader
reportID={REPORT_ID}
onRetry={jest.fn()}
/>,
);
expect(screen.getByTestId('ListBoundaryLoader')).toBeTruthy();
});

it('renders ConciergeThinkingMessage and delegates visibility gating to that component', () => {
render(
<ReportActionsListHeader
reportID={REPORT_ID}
onRetry={jest.fn()}
/>,
);
expect(screen.getByTestId('ConciergeThinkingMessage')).toBeTruthy();
});
});
Loading
Loading