Skip to content

Commit

Permalink
Merge pull request Expensify#31448 from infinitered/cdanwards/violati…
Browse files Browse the repository at this point in the history
…ons/transaction-thread-violations
  • Loading branch information
cead22 authored Jan 18, 2024
2 parents 9bd86e5 + 6e8ebca commit e6b0761
Show file tree
Hide file tree
Showing 28 changed files with 377 additions and 71 deletions.
8 changes: 7 additions & 1 deletion src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3069,7 +3069,8 @@ const CONST = {
},

/**
* Constants for maxToRenderPerBatch parameter that is used for FlatList or SectionList. This controls the amount of items rendered per batch, which is the next chunk of items rendered on every scroll.
* Constants for maxToRenderPerBatch parameter that is used for FlatList or SectionList. This controls the amount of items rendered per batch, which is the next chunk of items
* rendered on every scroll.
*/
MAX_TO_RENDER_PER_BATCH: {
DEFAULT: 5,
Expand All @@ -3081,6 +3082,11 @@ const CONST = {
RBR: 'RBR',
},

/**
* Constants for types of violations.
* Defined here because they need to be referenced by the type system to generate the
* ViolationNames type.
*/
VIOLATIONS: {
ALL_TAG_LEVELS_REQUIRED: 'allTagLevelsRequired',
AUTO_REPORTED_REJECTED_EXPENSE: 'autoReportedRejectedExpense',
Expand Down
1 change: 1 addition & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ type OnyxValues = {
[ONYXKEYS.COLLECTION.TRANSACTION_DRAFT]: OnyxTypes.Transaction;
[ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS]: OnyxTypes.RecentlyUsedTags;
[ONYXKEYS.COLLECTION.SELECTED_TAB]: string;
[ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS]: OnyxTypes.TransactionViolations;
[ONYXKEYS.COLLECTION.PRIVATE_NOTES_DRAFT]: string;
[ONYXKEYS.COLLECTION.NEXT_STEP]: OnyxTypes.ReportNextStep;

Expand Down
31 changes: 30 additions & 1 deletion src/components/LHNOptionsList/LHNOptionsList.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import _ from 'underscore';
import participantPropTypes from '@components/participantPropTypes';
import transactionPropTypes from '@components/transactionPropTypes';
import withCurrentReportID, {withCurrentReportIDDefaultProps, withCurrentReportIDPropTypes} from '@components/withCurrentReportID';
import usePermissions from '@hooks/usePermissions';
import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as ReportUtils from '@libs/ReportUtils';
import {transactionViolationsPropType} from '@libs/Violations/propTypes';
import reportActionPropTypes from '@pages/home/report/reportActionPropTypes';
import reportPropTypes from '@pages/reportPropTypes';
import stylePropTypes from '@styles/stylePropTypes';
Expand Down Expand Up @@ -63,8 +65,13 @@ const propTypes = {

/** The transaction from the parent report action */
transactions: PropTypes.objectOf(transactionPropTypes),

/** List of draft comments */
draftComments: PropTypes.objectOf(PropTypes.string),

/** The list of transaction violations */
transactionViolations: transactionViolationsPropType,

...withCurrentReportIDPropTypes,
};

Expand All @@ -78,6 +85,7 @@ const defaultProps = {
personalDetails: {},
transactions: {},
draftComments: {},
transactionViolations: {},
...withCurrentReportIDDefaultProps,
};

Expand All @@ -98,8 +106,10 @@ function LHNOptionsList({
transactions,
draftComments,
currentReportID,
transactionViolations,
}) {
const styles = useThemeStyles();
const {canUseViolations} = usePermissions();
/**
* Function which renders a row in the list
*
Expand Down Expand Up @@ -137,10 +147,26 @@ function LHNOptionsList({
onSelectRow={onSelectRow}
preferredLocale={preferredLocale}
comment={itemComment}
transactionViolations={transactionViolations}
canUseViolations={canUseViolations}
/>
);
},
[currentReportID, draftComments, onSelectRow, optionMode, personalDetails, policy, preferredLocale, reportActions, reports, shouldDisableFocusOptions, transactions],
[
currentReportID,
draftComments,
onSelectRow,
optionMode,
personalDetails,
policy,
preferredLocale,
reportActions,
reports,
shouldDisableFocusOptions,
transactions,
transactionViolations,
canUseViolations,
],
);

return (
Expand Down Expand Up @@ -189,5 +215,8 @@ export default compose(
draftComments: {
key: ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT,
},
transactionViolations: {
key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS,
},
}),
)(LHNOptionsList);
21 changes: 19 additions & 2 deletions src/components/LHNOptionsList/OptionRowLHNData.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import _ from 'underscore';
import participantPropTypes from '@components/participantPropTypes';
import transactionPropTypes from '@components/transactionPropTypes';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import SidebarUtils from '@libs/SidebarUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import {transactionViolationsPropType} from '@libs/Violations/propTypes';
import reportActionPropTypes from '@pages/home/report/reportActionPropTypes';
import * as Report from '@userActions/Report';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -42,6 +44,9 @@ const propTypes = {
/** The transaction from the parent report action */
transaction: transactionPropTypes,

/** Any violations associated with the transaction */
transactionViolations: transactionViolationsPropType,

...basePropTypes,
};

Expand Down Expand Up @@ -73,6 +78,8 @@ function OptionRowLHNData({
receiptTransactions,
parentReportAction,
transaction,
transactionViolations,
canUseViolations,
...propsToForward
}) {
const reportID = propsToForward.reportID;
Expand All @@ -85,9 +92,19 @@ function OptionRowLHNData({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fullReport.reportID, receiptTransactions, reportActions]);

const hasViolations = canUseViolations && ReportUtils.doesTransactionThreadHaveViolations(fullReport, transactionViolations, parentReportAction);

const optionItem = useMemo(() => {
// Note: ideally we'd have this as a dependent selector in onyx!
const item = SidebarUtils.getOptionData(fullReport, reportActions, personalDetails, preferredLocale, policy, parentReportAction);
const item = SidebarUtils.getOptionData({
report: fullReport,
reportActions,
personalDetails,
preferredLocale,
policy,
parentReportAction,
hasViolations,
});
if (deepEqual(item, optionItemRef.current)) {
return optionItemRef.current;
}
Expand All @@ -96,7 +113,7 @@ function OptionRowLHNData({
// Listen parentReportAction to update title of thread report when parentReportAction changed
// Listen to transaction to update title of transaction report when transaction changed
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fullReport, linkedTransaction, reportActions, personalDetails, preferredLocale, policy, parentReportAction, transaction]);
}, [fullReport, linkedTransaction, reportActions, personalDetails, preferredLocale, policy, parentReportAction, transaction, transactionViolations, canUseViolations]);

useEffect(() => {
if (!optionItem || optionItem.hasDraftComment || !comment || comment.length <= 0 || isFocused) {
Expand Down
14 changes: 13 additions & 1 deletion src/components/ReportActionItem/ReportPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Text from '@components/Text';
import transactionPropTypes from '@components/transactionPropTypes';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
import useLocalize from '@hooks/useLocalize';
import usePermissions from '@hooks/usePermissions';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
Expand All @@ -27,6 +28,7 @@ import * as ReceiptUtils from '@libs/ReceiptUtils';
import * as ReportActionUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
import {transactionViolationsPropType} from '@libs/Violations/propTypes';
import reportActionPropTypes from '@pages/home/report/reportActionPropTypes';
import reportPropTypes from '@pages/reportPropTypes';
import * as IOU from '@userActions/IOU';
Expand Down Expand Up @@ -108,6 +110,9 @@ const propTypes = {
/** All the transactions, used to update ReportPreview label and status */
transactions: PropTypes.objectOf(transactionPropTypes),

/** All of the transaction violations */
transactionViolations: transactionViolationsPropType,

...withLocalizePropTypes,
};

Expand All @@ -121,6 +126,9 @@ const defaultProps = {
accountID: null,
},
isWhisper: false,
transactionViolations: {
violations: [],
},
policy: {
isHarvestingEnabled: false,
},
Expand All @@ -131,6 +139,7 @@ function ReportPreview(props) {
const theme = useTheme();
const styles = useThemeStyles();
const {translate} = useLocalize();
const {canUseViolations} = usePermissions();

const {hasMissingSmartscanFields, areAllRequestsBeingSmartScanned, hasOnlyDistanceRequests, hasNonReimbursableTransactions} = useMemo(
() => ({
Expand Down Expand Up @@ -162,7 +171,7 @@ function ReportPreview(props) {
const numberOfScanningReceipts = _.filter(transactionsWithReceipts, (transaction) => TransactionUtils.isReceiptBeingScanned(transaction)).length;
const hasReceipts = transactionsWithReceipts.length > 0;
const isScanning = hasReceipts && areAllRequestsBeingSmartScanned;
const hasErrors = hasReceipts && hasMissingSmartscanFields;
const hasErrors = (hasReceipts && hasMissingSmartscanFields) || (canUseViolations && ReportUtils.hasViolations(props.iouReportID, props.transactionViolations));
const lastThreeTransactionsWithReceipts = transactionsWithReceipts.slice(-3);
const lastThreeReceipts = _.map(lastThreeTransactionsWithReceipts, (transaction) => ReceiptUtils.getThumbnailAndImageURIs(transaction));
let formattedMerchant = numberOfRequests === 1 && hasReceipts ? TransactionUtils.getMerchant(transactionsWithReceipts[0]) : null;
Expand Down Expand Up @@ -365,5 +374,8 @@ export default compose(
transactions: {
key: ONYXKEYS.COLLECTION.TRANSACTION,
},
transactionViolations: {
key: ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS,
},
}),
)(ReportPreview);
2 changes: 1 addition & 1 deletion src/components/ViolationMessages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, {useMemo} from 'react';
import {View} from 'react-native';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import ViolationsUtils from '@libs/ViolationsUtils';
import ViolationsUtils from '@libs/Violations/ViolationsUtils';
import type {TransactionViolation} from '@src/types/onyx';
import Text from './Text';

Expand Down
7 changes: 7 additions & 0 deletions src/hooks/__mocks__/usePermissions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @returns A mock of the usePermissions hook.
*/
const usePermissions = () => ({
canUseViolations: true,
});
export default usePermissions;
18 changes: 17 additions & 1 deletion src/libs/OptionsListUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -1292,6 +1292,7 @@ function getOptions(
recentlyUsedTags = [],
canInviteUser = true,
includeSelectedOptions = false,
transactionViolations = {},
includePolicyTaxRates,
policyTaxRates,
},
Expand Down Expand Up @@ -1357,7 +1358,22 @@ function getOptions(
const searchValue = parsedPhoneNumber.possible ? parsedPhoneNumber.number.e164 : searchInputValue.toLowerCase();

// Filter out all the reports that shouldn't be displayed
const filteredReports = _.filter(reports, (report) => ReportUtils.shouldReportBeInOptionList(report, Navigation.getTopmostReportId(), false, betas, policies));
const filteredReports = _.filter(reports, (report) => {
const {parentReportID, parentReportActionID} = report || {};
const canGetParentReport = parentReportID && parentReportActionID && allReportActions;
const parentReportAction = canGetParentReport ? lodashGet(allReportActions, [parentReportID, parentReportActionID], {}) : {};
const doesReportHaveViolations = betas.includes(CONST.BETAS.VIOLATIONS) && ReportUtils.doesTransactionThreadHaveViolations(report, transactionViolations, parentReportAction);

return ReportUtils.shouldReportBeInOptionList({
report,
currentReportId: Navigation.getTopmostReportId(),
betas,
policies,
doesReportHaveViolations,
isInGSDMode: false,
excludeEmptyChats: false,
});
});

// Sorting the reports works like this:
// - Order everything by the last message timestamp (descending)
Expand Down
Loading

0 comments on commit e6b0761

Please sign in to comment.