Skip to content

Commit

Permalink
Merge pull request #45112 from Expensify/Rory-DRYReportActionsSorting
Browse files Browse the repository at this point in the history
Create usePaginatedReportActions hook (round 2)
  • Loading branch information
bondydaa authored Jul 11, 2024
2 parents 3e3aa77 + 79949e5 commit 30462bc
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 57 deletions.
39 changes: 39 additions & 0 deletions src/hooks/usePaginatedReportActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {useMemo} from 'react';
import {useOnyx} from 'react-native-onyx';
import PaginationUtils from '@libs/PaginationUtils';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import ONYXKEYS from '@src/ONYXKEYS';

/**
* Get the longest continuous chunk of reportActions including the linked reportAction. If not linking to a specific action, returns the continuous chunk of newest reportActions.
*/
function usePaginatedReportActions(reportID?: string, reportActionID?: string) {
// Use `||` instead of `??` to handle empty string.
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const reportIDWithDefault = reportID || '-1';

const [sortedAllReportActions] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportIDWithDefault}`, {
canEvict: false,
selector: (allReportActions) => ReportActionsUtils.getSortedReportActionsForDisplay(allReportActions, true),
});
const [reportActionPages] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_PAGES}${reportIDWithDefault}`);

const reportActions = useMemo(() => {
if (!sortedAllReportActions?.length) {
return [];
}
return PaginationUtils.getContinuousChain(sortedAllReportActions, reportActionPages ?? [], (reportAction) => reportAction.reportActionID, reportActionID);
}, [reportActionID, reportActionPages, sortedAllReportActions]);

const linkedAction = useMemo(
() => sortedAllReportActions?.find((reportAction) => String(reportAction.reportActionID) === String(reportActionID)),
[reportActionID, sortedAllReportActions],
);

return {
reportActions,
linkedAction,
};
}

export default usePaginatedReportActions;
15 changes: 2 additions & 13 deletions src/pages/ReportDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ import ScrollView from '@components/ScrollView';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import usePaginatedReportActions from '@hooks/usePaginatedReportActions';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import type {ReportDetailsNavigatorParamList} from '@libs/Navigation/types';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import PaginationUtils from '@libs/PaginationUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
Expand Down Expand Up @@ -86,18 +86,7 @@ function ReportDetailsPage({policies, report, session, personalDetails}: ReportD
// The app would crash due to subscribing to the entire report collection if parentReportID is an empty string. So we should have a fallback ID here.
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${report.parentReportID || '-1'}`);
const [sortedAllReportActions = []] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${report.reportID || '-1'}`, {
canEvict: false,
selector: (allReportActions: OnyxEntry<OnyxTypes.ReportActions>) => ReportActionsUtils.getSortedReportActionsForDisplay(allReportActions, true),
});
const [reportActionPages = []] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS_PAGES}${report.reportID || '-1'}`);

const reportActions = useMemo(() => {
if (!sortedAllReportActions.length) {
return [];
}
return PaginationUtils.getContinuousChain(sortedAllReportActions, reportActionPages, (reportAction) => reportAction.reportActionID);
}, [sortedAllReportActions, reportActionPages]);
const {reportActions} = usePaginatedReportActions(report.reportID || '-1');

const transactionThreadReportID = useMemo(
() => ReportActionsUtils.getOneTransactionThreadReportID(report.reportID, reportActions ?? [], isOffline),
Expand Down
57 changes: 13 additions & 44 deletions src/pages/home/ReportScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,16 @@ import useDeepCompareRef from '@hooks/useDeepCompareRef';
import useIsReportOpenInRHP from '@hooks/useIsReportOpenInRHP';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import usePaginatedReportActions from '@hooks/usePaginatedReportActions';
import usePermissions from '@hooks/usePermissions';
import usePrevious from '@hooks/usePrevious';
import useThemeStyles from '@hooks/useThemeStyles';
import useViewportOffsetTop from '@hooks/useViewportOffsetTop';
import useWindowDimensions from '@hooks/useWindowDimensions';
import {getCurrentUserAccountID} from '@libs/actions/Report';
import Timing from '@libs/actions/Timing';
import Log from '@libs/Log';
import Navigation from '@libs/Navigation/Navigation';
import clearReportNotifications from '@libs/Notification/clearReportNotifications';
import PaginationUtils from '@libs/PaginationUtils';
import Performance from '@libs/Performance';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
Expand Down Expand Up @@ -70,12 +69,6 @@ type ReportScreenOnyxProps = {
/** The policies which the user has access to */
policies: OnyxCollection<OnyxTypes.Policy>;

/** An array containing all report actions related to this report, sorted based on a date criterion */
sortedAllReportActions: OnyxTypes.ReportAction[];

/** Pagination data for sortedAllReportActions */
reportActionPages: OnyxEntry<OnyxTypes.Pages>;

/** Additional report details */
reportNameValuePairs: OnyxEntry<OnyxTypes.ReportNameValuePairs>;

Expand Down Expand Up @@ -124,8 +117,6 @@ function ReportScreen({
betas = [],
route,
reportNameValuePairs,
sortedAllReportActions,
reportActionPages,
reportMetadata = {
isLoadingInitialReportActions: true,
isLoadingOlderReportActions: false,
Expand Down Expand Up @@ -290,12 +281,9 @@ function ReportScreen({
const prevReport = usePrevious(report);
const prevUserLeavingStatus = usePrevious(userLeavingStatus);
const [isLinkingToMessage, setIsLinkingToMessage] = useState(!!reportActionIDFromRoute);
const reportActions = useMemo(() => {
if (!sortedAllReportActions.length) {
return [];
}
return PaginationUtils.getContinuousChain(sortedAllReportActions, reportActionPages ?? [], (reportAction) => reportAction.reportActionID, reportActionIDFromRoute);
}, [reportActionIDFromRoute, sortedAllReportActions, reportActionPages]);

const [currentUserAccountID = -1] = useOnyx(ONYXKEYS.SESSION, {selector: (value) => value?.accountID});
const {reportActions, linkedAction} = usePaginatedReportActions(report.reportID, reportActionIDFromRoute);

// Define here because reportActions are recalculated before mount, allowing data to display faster than useEffect can trigger.
// If we have cached reportActions, they will be shown immediately.
Expand All @@ -320,10 +308,6 @@ function ReportScreen({
const screenWrapperStyle: ViewStyle[] = [styles.appContent, styles.flex1, {marginTop: viewportOffsetTop}];
const isEmptyChat = useMemo(() => ReportUtils.isEmptyReport(report), [report]);
const isOptimisticDelete = report.statusNum === CONST.REPORT.STATUS_NUM.CLOSED;
const isLinkedMessageAvailable = useMemo(
(): boolean => sortedAllReportActions.findIndex((obj) => String(obj.reportActionID) === String(reportActionIDFromRoute)) > -1,
[sortedAllReportActions, reportActionIDFromRoute],
);

// If there's a non-404 error for the report we should show it instead of blocking the screen
const hasHelpfulErrors = Object.keys(report?.errorFields ?? {}).some((key) => key !== 'notFound');
Expand Down Expand Up @@ -421,7 +405,7 @@ function ReportScreen({
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const isLoading = isLoadingApp || !reportIDFromRoute || (!isSidebarLoaded && !isReportOpenInRHP) || PersonalDetailsUtils.isPersonalDetailsEmpty();
const shouldShowSkeleton =
!isLinkedMessageAvailable &&
!linkedAction &&
(isLinkingToMessage ||
!isCurrentReportLoadedFromOnyx ||
(reportActions.length === 0 && !!reportMetadata?.isLoadingInitialReportActions) ||
Expand Down Expand Up @@ -691,28 +675,22 @@ function ReportScreen({
fetchReport();
}, [fetchReport]);

const {isLinkedReportActionDeleted, isInaccessibleWhisper} = useMemo(() => {
const currentUserAccountID = getCurrentUserAccountID();
if (!reportActionIDFromRoute || !sortedAllReportActions) {
return {isLinkedReportActionDeleted: false, isInaccessibleWhisper: false};
}
const action = sortedAllReportActions.find((item) => item.reportActionID === reportActionIDFromRoute);
return {
isLinkedReportActionDeleted: action && !ReportActionsUtils.shouldReportActionBeVisible(action, action.reportActionID),
isInaccessibleWhisper: action && ReportActionsUtils.isWhisperAction(action) && !(action?.whisperedToAccountIDs ?? []).includes(currentUserAccountID),
};
}, [reportActionIDFromRoute, sortedAllReportActions]);
const isLinkedActionDeleted = useMemo(() => !!linkedAction && !ReportActionsUtils.shouldReportActionBeVisible(linkedAction, linkedAction.reportActionID), [linkedAction]);
const isLinkedActionInaccessibleWhisper = useMemo(
() => !!linkedAction && ReportActionsUtils.isWhisperAction(linkedAction) && !(linkedAction?.whisperedToAccountIDs ?? []).includes(currentUserAccountID),
[currentUserAccountID, linkedAction],
);

// If user redirects to an inaccessible whisper via a deeplink, on a report they have access to,
// then we set reportActionID as empty string, so we display them the report and not the "Not found page".
useEffect(() => {
if (!isInaccessibleWhisper) {
if (!isLinkedActionInaccessibleWhisper) {
return;
}
Navigation.isNavigationReady().then(() => {
Navigation.setParams({reportActionID: ''});
});
}, [isInaccessibleWhisper]);
}, [isLinkedActionInaccessibleWhisper]);

useEffect(() => {
if (!!report.lastReadTime || !ReportUtils.isTaskReport(report)) {
Expand All @@ -722,7 +700,7 @@ function ReportScreen({
Report.readNewestAction(report.reportID);
}, [report]);

if ((!isInaccessibleWhisper && isLinkedReportActionDeleted) ?? (!shouldShowSkeleton && reportActionIDFromRoute && reportActions?.length === 0 && !isLinkingToMessage)) {
if ((!isLinkedActionInaccessibleWhisper && isLinkedActionDeleted) ?? (!shouldShowSkeleton && reportActionIDFromRoute && reportActions?.length === 0 && !isLinkingToMessage)) {
return (
<BlockingView
icon={Illustrations.ToddBehindCloud}
Expand Down Expand Up @@ -837,14 +815,6 @@ export default withCurrentReportID(
isSidebarLoaded: {
key: ONYXKEYS.IS_SIDEBAR_LOADED,
},
sortedAllReportActions: {
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getReportID(route)}`,
canEvict: false,
selector: (allReportActions: OnyxEntry<OnyxTypes.ReportActions>) => ReportActionsUtils.getSortedReportActionsForDisplay(allReportActions, true),
},
reportActionPages: {
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_ACTIONS_PAGES}${getReportID(route)}`,
},
reportNameValuePairs: {
key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${getReportID(route)}`,
allowStaleData: true,
Expand Down Expand Up @@ -873,7 +843,6 @@ export default withCurrentReportID(
ReportScreen,
(prevProps, nextProps) =>
prevProps.isSidebarLoaded === nextProps.isSidebarLoaded &&
lodashIsEqual(prevProps.sortedAllReportActions, nextProps.sortedAllReportActions) &&
lodashIsEqual(prevProps.reportMetadata, nextProps.reportMetadata) &&
lodashIsEqual(prevProps.betas, nextProps.betas) &&
lodashIsEqual(prevProps.policies, nextProps.policies) &&
Expand Down

0 comments on commit 30462bc

Please sign in to comment.