Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TS migration] Migrate MoneyTemporaryForRefactorRequestParticipantsSe… #41216

Merged
Show file tree
Hide file tree
Changes from 4 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
3 changes: 2 additions & 1 deletion src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4722,7 +4722,8 @@ type Country = keyof typeof CONST.ALL_COUNTRIES;

type IOUType = ValueOf<typeof CONST.IOU.TYPE>;
type IOUAction = ValueOf<typeof CONST.IOU.ACTION>;
type IOURequestType = ValueOf<typeof CONST.IOU.REQUEST_TYPE>;

export type {Country, IOUAction, IOUType, RateAndUnit, OnboardingPurposeType};
export type {Country, IOUAction, IOUType, RateAndUnit, OnboardingPurposeType, IOURequestType};

export default CONST;
4 changes: 2 additions & 2 deletions src/components/ReferralProgramCTA.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, {useEffect} from 'react';
import type {ViewStyle} from 'react-native';
import type {StyleProp, ViewStyle} from 'react-native';
import useDismissedReferralBanners from '@hooks/useDismissedReferralBanners';
import useLocalize from '@hooks/useLocalize';
import useTheme from '@hooks/useTheme';
Expand All @@ -19,7 +19,7 @@ type ReferralProgramCTAProps = {
| typeof CONST.REFERRAL_PROGRAM.CONTENT_TYPES.START_CHAT
| typeof CONST.REFERRAL_PROGRAM.CONTENT_TYPES.PAY_SOMEONE
| typeof CONST.REFERRAL_PROGRAM.CONTENT_TYPES.REFER_FRIEND;
style?: ViewStyle;
style?: StyleProp<ViewStyle>;
onDismiss?: () => void;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import lodashGet from 'lodash/get';
import lodashIsEqual from 'lodash/isEqual';
import lodashMap from 'lodash/map';
import lodashPick from 'lodash/pick';
import lodashReject from 'lodash/reject';
import lodashSome from 'lodash/some';
import lodashValues from 'lodash/values';
import PropTypes from 'prop-types';
import React, {memo, useCallback, useEffect, useMemo} from 'react';
import type {GestureResponderEvent} from 'react-native';
import {useOnyx} from 'react-native-onyx';
import Button from '@components/Button';
import FormHelpMessage from '@components/FormHelpMessage';
Expand All @@ -23,46 +19,43 @@ import usePermissions from '@hooks/usePermissions';
import useScreenWrapperTranstionStatus from '@hooks/useScreenWrapperTransitionStatus';
import useThemeStyles from '@hooks/useThemeStyles';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import type {MaybePhraseKey} from '@libs/Localize';
import type {Options} from '@libs/OptionsListUtils';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import * as Policy from '@userActions/Policy';
import * as Report from '@userActions/Report';
import type {IOUAction, IOURequestType, IOUType} from '@src/CONST';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Participant} from '@src/types/onyx/IOU';

const propTypes = {
type MoneyRequestParticipantsSelectorProps = {
/** Callback to request parent modal to go to next step, which should be split */
onFinish: PropTypes.func.isRequired,
onFinish: (value?: string) => void;

/** Callback to add participants in MoneyRequestModal */
onParticipantsAdded: PropTypes.func.isRequired,

onParticipantsAdded: (value: Participant[]) => void;
/** Selected participants from MoneyRequestModal with login */
participants: PropTypes.arrayOf(
PropTypes.shape({
accountID: PropTypes.number,
login: PropTypes.string,
isPolicyExpenseChat: PropTypes.bool,
isOwnPolicyExpenseChat: PropTypes.bool,
selected: PropTypes.bool,
}),
),
participants?: Participant[];

/** The type of IOU report, i.e. split, request, send, track */
iouType: PropTypes.oneOf(lodashValues(CONST.IOU.TYPE)).isRequired,
iouType: IOUType;

/** The expense type, ie. manual, scan, distance */
iouRequestType: PropTypes.oneOf(lodashValues(CONST.IOU.REQUEST_TYPE)).isRequired,
iouRequestType: IOURequestType;

/** The action of the IOU, i.e. create, split, move */
action: PropTypes.oneOf(lodashValues(CONST.IOU.ACTION)),
};

const defaultProps = {
participants: [],
action: CONST.IOU.ACTION.CREATE,
action?: IOUAction;
};
pasyukevich marked this conversation as resolved.
Show resolved Hide resolved

function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onFinish, onParticipantsAdded, iouType, iouRequestType, action}) {
function MoneyRequestParticipantsSelector({
participants = [],
onFinish,
onParticipantsAdded,
iouType,
iouRequestType,
action = CONST.IOU.ACTION.CREATE,
}: MoneyRequestParticipantsSelectorProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState('');
Expand All @@ -79,10 +72,10 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
shouldInitialize: didScreenTransitionEnd,
});

const offlineMessage = isOffline ? [`${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}`, {isTranslated: true}] : '';
const offlineMessage: MaybePhraseKey = isOffline ? [`${translate('common.youAppearToBeOffline')} ${translate('search.resultsAreLimited')}`, {isTranslated: true}] : '';

const isIOUSplit = iouType === CONST.IOU.TYPE.SPLIT;
const isCategorizeOrShareAction = [CONST.IOU.ACTION.CATEGORIZE, CONST.IOU.ACTION.SHARE].includes(action);
const isCategorizeOrShareAction = [CONST.IOU.ACTION.CATEGORIZE, CONST.IOU.ACTION.SHARE].some((option) => option === action);

const shouldShowReferralBanner = !isDismissed && iouType !== CONST.IOU.TYPE.INVOICE;

Expand All @@ -96,7 +89,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
* @returns {Array}
*/
const [sections, newChatOptions] = useMemo(() => {
const newSections = [];
const newSections: OptionsListUtils.CategorySection[] = [];
if (!areOptionsInitialized || !didScreenTransitionEnd) {
return [newSections, {}];
}
Expand All @@ -112,13 +105,15 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
// sees the option to submit an expense from their admin on their own Workspace Chat.
(iouType === CONST.IOU.TYPE.SUBMIT || iouType === CONST.IOU.TYPE.SPLIT) && action !== CONST.IOU.ACTION.SUBMIT,

// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
(canUseP2PDistanceRequests || iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE) && !isCategorizeOrShareAction,
false,
{},
[],
false,
{},
[],
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
(canUseP2PDistanceRequests || iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE) && !isCategorizeOrShareAction,
false,
undefined,
Expand All @@ -131,7 +126,14 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
isCategorizeOrShareAction ? 0 : undefined,
);

const formatResults = OptionsListUtils.formatSectionsFromSearchTerm(debouncedSearchTerm, participants, chatOptions.recentReports, chatOptions.personalDetails, personalDetails, true);
const formatResults = OptionsListUtils.formatSectionsFromSearchTerm(
debouncedSearchTerm,
participants.map((participant) => ({...participant, reportID: participant.reportID ?? ''})),
chatOptions.recentReports,
chatOptions.personalDetails,
personalDetails,
true,
);

newSections.push(formatResults.section);

Expand All @@ -147,11 +149,14 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
shouldShow: chatOptions.personalDetails.length > 0,
});

if (chatOptions.userToInvite && !OptionsListUtils.isCurrentUser(chatOptions.userToInvite)) {
if (
chatOptions.userToInvite &&
!OptionsListUtils.isCurrentUser({...chatOptions.userToInvite, accountID: chatOptions.userToInvite?.accountID ?? -1, status: chatOptions.userToInvite?.status ?? undefined})
) {
newSections.push({
title: undefined,
data: lodashMap([chatOptions.userToInvite], (participant) => {
const isPolicyExpenseChat = lodashGet(participant, 'isPolicyExpenseChat', false);
data: [chatOptions.userToInvite].map((participant) => {
const isPolicyExpenseChat = participant?.isPolicyExpenseChat ?? false;
return isPolicyExpenseChat ? OptionsListUtils.getPolicyExpenseReportOption(participant) : OptionsListUtils.getParticipantsOption(participant, personalDetails);
}),
shouldShow: true,
Expand Down Expand Up @@ -182,8 +187,8 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
* @param {Object} option
*/
const addSingleParticipant = useCallback(
(option) => {
const newParticipants = [
(option: Participant) => {
const newParticipants: Participant[] = [
{
...lodashPick(option, 'accountID', 'login', 'isPolicyExpenseChat', 'reportID', 'searchText', 'policyID'),
selected: true,
Expand All @@ -192,10 +197,10 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
];

if (iouType === CONST.IOU.TYPE.INVOICE) {
const primaryPolicy = Policy.getPrimaryPolicy(activePolicyID);
const primaryPolicy = Policy.getPrimaryPolicy(activePolicyID ?? undefined);

pasyukevich marked this conversation as resolved.
Show resolved Hide resolved
newParticipants.push({
policyID: primaryPolicy.id,
policyID: primaryPolicy?.id,
isSender: true,
selected: false,
iouType,
Expand All @@ -214,20 +219,20 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
* @param {Object} option
*/
const addParticipantToSelection = useCallback(
(option) => {
const isOptionSelected = (selectedOption) => {
if (selectedOption.accountID && selectedOption.accountID === option.accountID) {
(option: Participant) => {
const isOptionSelected = (selectedOption: Participant) => {
if (selectedOption.accountID && selectedOption.accountID === option?.accountID) {
return true;
}

if (selectedOption.reportID && selectedOption.reportID === option.reportID) {
if (selectedOption.reportID && selectedOption.reportID === option?.reportID) {
return true;
}

return false;
};
const isOptionInList = lodashSome(participants, isOptionSelected);
let newSelectedOptions;
const isOptionInList = participants.some(isOptionSelected);
let newSelectedOptions: Participant[];

if (isOptionInList) {
newSelectedOptions = lodashReject(participants, isOptionSelected);
Expand Down Expand Up @@ -255,28 +260,28 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
const headerMessage = useMemo(
() =>
OptionsListUtils.getHeaderMessage(
lodashGet(newChatOptions, 'personalDetails', []).length + lodashGet(newChatOptions, 'recentReports', []).length !== 0,
Boolean(newChatOptions.userToInvite),
((newChatOptions as Options)?.personalDetails ?? []).length + ((newChatOptions as Options)?.recentReports ?? []).length !== 0,
Boolean((newChatOptions as Options)?.userToInvite),
debouncedSearchTerm.trim(),
pasyukevich marked this conversation as resolved.
Show resolved Hide resolved
lodashSome(participants, (participant) => participant.searchText.toLowerCase().includes(debouncedSearchTerm.trim().toLowerCase())),
participants.some((participant) => participant?.searchText?.toLowerCase().includes(debouncedSearchTerm.trim().toLowerCase())),
),
[newChatOptions, participants, debouncedSearchTerm],
);

// Right now you can't split an expense with a workspace and other additional participants
// This is getting properly fixed in https://github.com/Expensify/App/issues/27508, but as a stop-gap to prevent
// the app from crashing on native when you try to do this, we'll going to hide the button if you have a workspace and other participants
const hasPolicyExpenseChatParticipant = lodashSome(participants, (participant) => participant.isPolicyExpenseChat);
const hasPolicyExpenseChatParticipant = participants.some((participant) => participant.isPolicyExpenseChat);
const shouldShowSplitBillErrorMessage = participants.length > 1 && hasPolicyExpenseChatParticipant;

// canUseP2PDistanceRequests is true if the iouType is track expense, but we don't want to allow splitting distance with track expense yet
const isAllowedToSplit =
(canUseP2PDistanceRequests || iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE) &&
![CONST.IOU.TYPE.PAY, CONST.IOU.TYPE.TRACK, CONST.IOU.TYPE.INVOICE].includes(iouType) &&
![CONST.IOU.ACTION.SHARE, CONST.IOU.ACTION.SUBMIT, CONST.IOU.ACTION.CATEGORIZE].includes(action);
(canUseP2PDistanceRequests ?? iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE) &&
![CONST.IOU.TYPE.PAY, CONST.IOU.TYPE.TRACK, CONST.IOU.TYPE.INVOICE].some((option) => option === iouType) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using ?? will change the behavior, could you help to explain about this change?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It caused this bug: Can't make multiple selection

Screen.Recording.2024-05-07.at.15.30.00.mov

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted

There is no need for this change, missed logic during the migration

![CONST.IOU.ACTION.SHARE, CONST.IOU.ACTION.SUBMIT, CONST.IOU.ACTION.CATEGORIZE].some((option) => option === action);

const handleConfirmSelection = useCallback(
(keyEvent, option) => {
(keyEvent?: GestureResponderEvent | KeyboardEvent, option?: Participant) => {
const shouldAddSingleParticipant = option && !participants.length;
if (shouldShowSplitBillErrorMessage || (!participants.length && !option)) {
return;
Expand Down Expand Up @@ -348,15 +353,10 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF
);
}

MoneyTemporaryForRefactorRequestParticipantsSelector.propTypes = propTypes;
MoneyTemporaryForRefactorRequestParticipantsSelector.defaultProps = defaultProps;
MoneyTemporaryForRefactorRequestParticipantsSelector.displayName = 'MoneyTemporaryForRefactorRequestParticipantsSelector';
MoneyRequestParticipantsSelector.displayName = 'MoneyTemporaryForRefactorRequestParticipantsSelector';

export default memo(
MoneyTemporaryForRefactorRequestParticipantsSelector,
MoneyRequestParticipantsSelector,
(prevProps, nextProps) =>
lodashIsEqual(prevProps.participants, nextProps.participants) &&
prevProps.iouRequestType === nextProps.iouRequestType &&
prevProps.iouType === nextProps.iouType &&
lodashIsEqual(prevProps.betas, nextProps.betas),
lodashIsEqual(prevProps.participants, nextProps.participants) && prevProps.iouRequestType === nextProps.iouRequestType && prevProps.iouType === nextProps.iouType,
);
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import DistanceRequestUtils from '@libs/DistanceRequestUtils';
import * as IOUUtils from '@libs/IOUUtils';
import Navigation from '@libs/Navigation/Navigation';
import * as TransactionUtils from '@libs/TransactionUtils';
import MoneyRequestParticipantsSelector from '@pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector';
import MoneyRequestParticipantsSelector from '@pages/iou/request/MoneyRequestParticipantsSelector';
import * as IOU from '@userActions/IOU';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
Expand Down
1 change: 1 addition & 0 deletions src/types/onyx/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type Participant = {
isSelected?: boolean;
isSelfDM?: boolean;
isSender?: boolean;
iouType?: string;
};

type Split = {
Expand Down
Loading