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

Workspaces in money request modal #17132

Merged
merged 29 commits into from
Apr 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1d602e4
Rename participants to selectedOptions in MoneyRequestModal.js
cristipaval Apr 6, 2023
bddf4b0
set optiotPropTypes as the prop type for participants
cristipaval Apr 6, 2023
80efa65
Rename steps in MoneyRequestModal
cristipaval Apr 6, 2023
22f6234
Rename IOUAmountPage to MoneyRequestAmountPage
cristipaval Apr 6, 2023
964c5ce
Rename IOUConfirmPage to MoneyRequestConfirmPage
cristipaval Apr 6, 2023
b31fb44
Rename IOUParticipantsPage to MoneyRequestParticipantsPage
cristipaval Apr 6, 2023
1d9f7be
WIP.
cristipaval Apr 7, 2023
bb95eae
Merge remote-tracking branch 'origin/main' into cristi_workspaces-in-…
cristipaval Apr 7, 2023
dd7da6a
MoneyRequestModal now supports workspaces.
cristipaval Apr 7, 2023
ca7d9c7
Rename personalDetails to option
cristipaval Apr 7, 2023
13ff8e3
handle option icons and alternateText
cristipaval Apr 7, 2023
b1654c5
Make Lint happy
cristipaval Apr 7, 2023
54e5be9
Merge remote-tracking branch 'origin/main' into cristi_workspaces-in-…
cristipaval Apr 10, 2023
c59279a
Do not request money if the participant is a workspace.
cristipaval Apr 10, 2023
ba9741a
Add issue in the comment.
cristipaval Apr 12, 2023
33969d7
Update src/libs/OptionsListUtils.js
cristipaval Apr 12, 2023
fcda6a8
Update src/libs/OptionsListUtils.js
cristipaval Apr 12, 2023
3c9ebb2
Update src/pages/iou/MoneyRequestModal.js
cristipaval Apr 12, 2023
e813f18
Update src/pages/iou/MoneyRequestModal.js
cristipaval Apr 12, 2023
a42f122
Rename iou to moneyRequest in translation keys.
cristipaval Apr 12, 2023
f5aa96e
Add phoneNumber and payPalMeAddress props to OptionPropTypes
cristipaval Apr 12, 2023
37e7740
Better naming.
cristipaval Apr 12, 2023
b3e2660
Better naming.
cristipaval Apr 12, 2023
3278622
Optimize function call
cristipaval Apr 12, 2023
c200d93
Move mapping from MoneyRequestModal to OptionsListUtils
cristipaval Apr 12, 2023
41b9d74
Update src/libs/OptionsListUtils.js
cristipaval Apr 13, 2023
ffd15bf
Update src/libs/OptionsListUtils.js
cristipaval Apr 13, 2023
4f995a9
Remove useless comments.
cristipaval Apr 13, 2023
9754a32
Avoid repeating logic at every component render.
cristipaval Apr 13, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import SettlementButton from './SettlementButton';
import ROUTES from '../ROUTES';
import withCurrentUserPersonalDetails, {withCurrentUserPersonalDetailsPropTypes, withCurrentUserPersonalDetailsDefaultProps} from './withCurrentUserPersonalDetails';
import * as IOUUtils from '../libs/IOUUtils';
import avatarPropTypes from './avatarPropTypes';
import optionPropTypes from './optionPropTypes';

const propTypes = {
/** Callback to inform parent modal of success */
Expand All @@ -41,20 +41,7 @@ const propTypes = {
iouType: PropTypes.string,

/** Selected participants from MoneyRequestModal with login */
participants: PropTypes.arrayOf(PropTypes.shape({
login: PropTypes.string.isRequired,
alternateText: PropTypes.string,
hasDraftComment: PropTypes.bool,
icons: PropTypes.arrayOf(avatarPropTypes),
searchText: PropTypes.string,
text: PropTypes.string,
keyForList: PropTypes.string,
reportID: PropTypes.string,
// eslint-disable-next-line react/forbid-prop-types
participantsList: PropTypes.arrayOf(PropTypes.object),
payPalMeAddress: PropTypes.string,
phoneNumber: PropTypes.string,
})).isRequired,
participants: PropTypes.arrayOf(optionPropTypes).isRequired,

/** Can the participants be modified or not */
canModifyParticipants: PropTypes.bool,
Expand Down Expand Up @@ -97,7 +84,7 @@ const defaultProps = {
...withCurrentUserPersonalDetailsDefaultProps,
};

class IOUConfirmationList extends Component {
class MoneyRequestConfirmationList extends Component {
cristipaval marked this conversation as resolved.
Show resolved Hide resolved
constructor(props) {
super(props);

Expand Down Expand Up @@ -194,13 +181,13 @@ class IOUConfirmationList extends Component {
);

sections.push({
title: this.props.translate('iOUConfirmationList.whoPaid'),
title: this.props.translate('moneyRequestConfirmationList.whoPaid'),
data: [formattedMyPersonalDetails],
shouldShow: true,
indexOffset: 0,
isDisabled: true,
}, {
title: this.props.translate('iOUConfirmationList.whoWasThere'),
title: this.props.translate('moneyRequestConfirmationList.whoWasThere'),
data: formattedParticipants,
shouldShow: true,
indexOffset: 1,
Expand Down Expand Up @@ -293,7 +280,7 @@ class IOUConfirmationList extends Component {
onSelectRow={canModifyParticipants ? this.toggleOption : undefined}
onConfirmSelection={this.confirm}
onChangeText={this.props.onUpdateComment}
textInputLabel={this.props.translate('iOUConfirmationList.whatsItFor')}
textInputLabel={this.props.translate('moneyRequestConfirmationList.whatsItFor')}
placeholderText={this.props.translate('common.optional')}
selectedOptions={this.getSelectedOptions()}
canSelectMultipleOptions={canModifyParticipants}
Expand Down Expand Up @@ -327,8 +314,8 @@ class IOUConfirmationList extends Component {
}
}

IOUConfirmationList.propTypes = propTypes;
IOUConfirmationList.defaultProps = defaultProps;
MoneyRequestConfirmationList.propTypes = propTypes;
MoneyRequestConfirmationList.defaultProps = defaultProps;

export default compose(
withLocalize,
Expand All @@ -340,4 +327,4 @@ export default compose(
key: ONYXKEYS.SESSION,
},
}),
)(IOUConfirmationList);
)(MoneyRequestConfirmationList);
4 changes: 4 additions & 0 deletions src/components/optionPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,8 @@ export default PropTypes.shape({

/** If we need to show a brick road indicator or not */
brickRoadIndicator: PropTypes.oneOf([CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR, '']),

phoneNumber: PropTypes.string,

payPalMeAddress: PropTypes.string,
});
2 changes: 1 addition & 1 deletion src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export default {
tfaRequiredTitle: 'Two factor authentication\nrequired',
tfaRequiredDescription: 'Please enter the two-factor authentication code\nwhere you are trying to sign in.',
},
iOUConfirmationList: {
moneyRequestConfirmationList: {
whoPaid: 'Who paid?',
whoWasThere: 'Who was there?',
whatsItFor: 'What\'s it for?',
Expand Down
2 changes: 1 addition & 1 deletion src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export default {
tfaRequiredTitle: 'Se requiere autenticación\nde dos factores',
tfaRequiredDescription: 'Por favor, introduce el código de autenticación de dos factores\ndonde estás intentando iniciar sesión.',
},
iOUConfirmationList: {
moneyRequestConfirmationList: {
whoPaid: '¿Quién pago?',
whoWasThere: '¿Quién asistió?',
whatsItFor: '¿Para qué es?',
Expand Down
67 changes: 67 additions & 0 deletions src/libs/OptionsListUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import _ from 'underscore';
import Onyx from 'react-native-onyx';
import lodashOrderBy from 'lodash/orderBy';
import lodashGet from 'lodash/get';
import Str from 'expensify-common/lib/str';
import ONYXKEYS from '../ONYXKEYS';
import CONST from '../CONST';
Expand Down Expand Up @@ -77,6 +78,45 @@ Onyx.connect({
},
});

const policyExpenseReports = {};
Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT,
callback: (report, key) => {
if (!ReportUtils.isPolicyExpenseChat(report)) {
return;
}
policyExpenseReports[key] = report;
},
});
luacmartins marked this conversation as resolved.
Show resolved Hide resolved

/**
* Get the options for a policy expense report.
* @param {Object} report
* @returns {Array}
*/
function getPolicyExpenseReportOptions(report) {
cristipaval marked this conversation as resolved.
Show resolved Hide resolved
if (!ReportUtils.isPolicyExpenseChat(report)) {
return [];
}
const filteredPolicyExpenseReports = _.filter(policyExpenseReports, policyExpenseReport => policyExpenseReport.policyID === report.policyID);
return _.map(filteredPolicyExpenseReports, (expenseReport) => {
const policyExpenseChatAvatarSource = lodashGet(policies, [
`${ONYXKEYS.COLLECTION.POLICY}${expenseReport.policyID}`, 'avatar',
]) || ReportUtils.getDefaultWorkspaceAvatar(expenseReport.displayName);
return {
...expenseReport,
cristipaval marked this conversation as resolved.
Show resolved Hide resolved
keyForList: expenseReport.policyID,
text: expenseReport.displayName,
Copy link
Contributor

Choose a reason for hiding this comment

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

Not exactly a regression but we shouldn't have used expenseReport.displayName here because the display name is not necessary the policy name. The display name is the policy name only if you are the policy expense chat owner.

This worked fine with money request because you are the only one who can see the participants list (and that's only at the creation phase). But that's not the case with split action.

alternateText: Localize.translateLocal('workspace.common.workspace'),
icons: [{
source: policyExpenseChatAvatarSource,
name: expenseReport.displayName,
type: CONST.ICON_TYPE_WORKSPACE,
}],
Comment on lines +111 to +115
Copy link
Contributor

Choose a reason for hiding this comment

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

We should have used the already existing createOption to generate these options. Using the icons object like this caused #28731

};
});
}

/**
* Adds expensify SMS domain (@expensify.sms) if login is a phone number and if it's not included yet
*
Expand Down Expand Up @@ -126,6 +166,31 @@ function getPersonalDetailsForLogins(logins, personalDetails) {
return personalDetailsForLogins;
}

/**
* Get the participant options for a report.
* @param {Object} report
* @param {Array<Object>} personalDetails
* @returns {Array}
*/
function getParticipantsOptions(report, personalDetails) {
cristipaval marked this conversation as resolved.
Show resolved Hide resolved
const participants = lodashGet(report, 'participants', []);
return _.map(getPersonalDetailsForLogins(participants, personalDetails), details => ({
keyForList: details.login,
login: details.login,
text: details.displayName,
firstName: lodashGet(details, 'firstName', ''),
lastName: lodashGet(details, 'lastName', ''),
alternateText: Str.isSMSLogin(details.login) ? Str.removeSMSDomain(details.login) : details.login,
icons: [{
source: ReportUtils.getAvatar(details.avatar, details.login),
name: details.login,
type: CONST.ICON_TYPE_AVATAR,
}],
payPalMeAddress: lodashGet(details, 'payPalMeAddress', ''),
phoneNumber: lodashGet(details, 'phoneNumber', ''),
}));
Copy link
Contributor

Choose a reason for hiding this comment

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

Just wondering: Any reason we didn't add isMoneyRequestReport: true here? Since I believe this is only used in money request reports

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe because of the split case, since those don't create an IOU/Expense report for the group chat?

Copy link
Contributor

Choose a reason for hiding this comment

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

innnnnnnnnnnnnnnnnnnnnnnteresting that would make sense :D

}

/**
* Constructs a Set with all possible names (displayName, firstName, lastName, email) for all participants in a report,
* to be used in isSearchStringMatch.
Expand Down Expand Up @@ -819,4 +884,6 @@ export {
getIOUConfirmationOptionsFromParticipants,
getSearchText,
getAllReportErrors,
getPolicyExpenseReportOptions,
getParticipantsOptions,
};
66 changes: 29 additions & 37 deletions src/pages/iou/MoneyRequestModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import {View} from 'react-native';
import PropTypes from 'prop-types';
import lodashGet from 'lodash/get';
import {withOnyx} from 'react-native-onyx';
import Str from 'expensify-common/lib/str';
import IOUAmountPage from './steps/IOUAmountPage';
import IOUParticipantsPage from './steps/IOUParticipantsPage/IOUParticipantsPage';
import IOUConfirmPage from './steps/IOUConfirmPage';
import MoneyRequestAmountPage from './steps/MoneyRequestAmountPage';
import MoneyRequestParticipantsPage from './steps/MoneyRequstParticipantsPage/MoneyRequestParticipantsPage';
import MoneyRequestConfirmPage from './steps/MoneyRequestConfirmPage';
import ModalHeader from './ModalHeader';
import styles from '../../styles/styles';
import * as IOU from '../../libs/actions/IOU';
Expand Down Expand Up @@ -99,38 +98,26 @@ const defaultProps = {

// Determines type of step to display within Modal, value provides the title for that page.
const Steps = {
IOUAmount: 'iou.amount',
IOUParticipants: 'iou.participants',
IOUConfirm: 'iou.confirm',
MoneyRequestAmount: 'moneyRequest.amount',
MoneyRequestParticipants: 'moneyRequest.participants',
MoneyRequestConfirm: 'moneyRequest.confirm',
};

const MoneyRequestModal = (props) => {
const reportParticipants = lodashGet(props, 'report.participants', []);
const participantsWithDetails = _.map(OptionsListUtils.getPersonalDetailsForLogins(reportParticipants, props.personalDetails), personalDetails => ({
login: personalDetails.login,
text: personalDetails.displayName,
firstName: lodashGet(personalDetails, 'firstName', ''),
lastName: lodashGet(personalDetails, 'lastName', ''),
alternateText: Str.isSMSLogin(personalDetails.login) ? Str.removeSMSDomain(personalDetails.login) : personalDetails.login,
icons: [{
cristipaval marked this conversation as resolved.
Show resolved Hide resolved
source: ReportUtils.getAvatar(personalDetails.avatar, personalDetails.login),
name: personalDetails.login,
type: CONST.ICON_TYPE_AVATAR,
}],
keyForList: personalDetails.login,
payPalMeAddress: lodashGet(personalDetails, 'payPalMeAddress', ''),
phoneNumber: lodashGet(personalDetails, 'phoneNumber', ''),
}));

// Skip IOUParticipants step if participants are passed in
const steps = reportParticipants.length ? [Steps.IOUAmount, Steps.IOUConfirm] : [Steps.IOUAmount, Steps.IOUParticipants, Steps.IOUConfirm];
const reportParticipants = lodashGet(props, 'report.participants', []);
const steps = reportParticipants.length ? [Steps.MoneyRequestAmount, Steps.MoneyRequestConfirm] : [Steps.MoneyRequestAmount, Steps.MoneyRequestParticipants, Steps.MoneyRequestConfirm];

const prevCreatingIOUTransactionStatusRef = useRef(lodashGet(props.iou, 'creatingIOUTransaction'));
const prevNetworkStatusRef = useRef(props.network.isOffline);

const [previousStepIndex, setPreviousStepIndex] = useState(0);
const [currentStepIndex, setCurrentStepIndex] = useState(0);
const [participants, setParticipants] = useState(participantsWithDetails);
const [selectedOptions, setSelectedOptions] = useState(
ReportUtils.isPolicyExpenseChat(props.report)
? OptionsListUtils.getPolicyExpenseReportOptions(props.report)
: OptionsListUtils.getParticipantsOptions(props.report, props.personalDetails),
);
const [amount, setAmount] = useState('');
const [comment, setComment] = useState('');

Expand Down Expand Up @@ -257,7 +244,7 @@ const MoneyRequestModal = (props) => {
const amountInDollars = Math.round(amount * 100);
const currency = props.iou.selectedCurrencyCode;
const trimmedComment = comment.trim();
const participant = participants[0];
const participant = selectedOptions[0];

if (paymentMethodType === CONST.IOU.PAYMENT_TYPE.ELSEWHERE) {
IOU.sendMoneyElsewhere(
Expand Down Expand Up @@ -293,7 +280,7 @@ const MoneyRequestModal = (props) => {
participant,
);
}
}, [amount, comment, participants, props.currentUserPersonalDetails.login, props.iou.selectedCurrencyCode, props.report]);
}, [amount, comment, selectedOptions, props.currentUserPersonalDetails.login, props.iou.selectedCurrencyCode, props.report]);

/**
* @param {Array} selectedParticipants
Expand Down Expand Up @@ -329,6 +316,11 @@ const MoneyRequestModal = (props) => {
);
return;
}
if (!selectedParticipants[0].login) {
// TODO - request to the policy expense chat. Not implemented yet!
cristipaval marked this conversation as resolved.
Show resolved Hide resolved
// Will be implemented here: https://github.com/Expensify/Expensify/issues/270581
return;
}
IOU.requestMoney(
props.report,
Math.round(amount * 100),
Expand All @@ -351,13 +343,13 @@ const MoneyRequestModal = (props) => {
{!didScreenTransitionEnd && <FullScreenLoadingIndicator />}
{didScreenTransitionEnd && (
<>
{currentStep === Steps.IOUAmount && (
{currentStep === Steps.MoneyRequestAmount && (
<AnimatedStep
direction={direction}
style={[styles.flex1, safeAreaPaddingBottomStyle]}
>
{modalHeader}
<IOUAmountPage
<MoneyRequestAmountPage
onStepComplete={(value) => {
setAmount(value);
navigateToNextStep();
Expand All @@ -370,29 +362,29 @@ const MoneyRequestModal = (props) => {
/>
</AnimatedStep>
)}
{currentStep === Steps.IOUParticipants && (
{currentStep === Steps.MoneyRequestParticipants && (
<AnimatedStep
style={[styles.flex1]}
direction={direction}
>
{modalHeader}
<IOUParticipantsPage
participants={participants}
<MoneyRequestParticipantsPage
participants={selectedOptions}
hasMultipleParticipants={props.hasMultipleParticipants}
onAddParticipants={selectedParticipants => setParticipants(selectedParticipants)}
onAddParticipants={setSelectedOptions}
onStepComplete={navigateToNextStep}
safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle}
iouType={props.iouType}
/>
</AnimatedStep>
)}
{currentStep === Steps.IOUConfirm && (
{currentStep === Steps.MoneyRequestConfirm && (
<AnimatedStep
style={[styles.flex1, safeAreaPaddingBottomStyle]}
direction={direction}
>
{modalHeader}
<IOUConfirmPage
<MoneyRequestConfirmPage
onConfirm={(selectedParticipants) => {
// TODO: ADD HANDLING TO DISABLE BUTTON FUNCTIONALITY WHILE REQUEST IS IN FLIGHT
createTransaction(selectedParticipants);
Expand All @@ -404,7 +396,7 @@ const MoneyRequestModal = (props) => {
ReportScrollManager.scrollToBottom();
}}
hasMultipleParticipants={props.hasMultipleParticipants}
participants={_.filter(participants, email => props.currentUserPersonalDetails.login !== email.login)}
participants={_.filter(selectedOptions, email => props.currentUserPersonalDetails.login !== email.login)}
iouAmount={amount}
comment={comment}
onUpdateComment={value => setComment(value)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const defaultProps = {
selectedCurrencyCode: CONST.CURRENCY.USD,
},
};
class IOUAmountPage extends React.Component {
class MoneyRequestAmountPage extends React.Component {
constructor(props) {
super(props);

Expand Down Expand Up @@ -344,12 +344,12 @@ class IOUAmountPage extends React.Component {
}
}

IOUAmountPage.propTypes = propTypes;
IOUAmountPage.defaultProps = defaultProps;
MoneyRequestAmountPage.propTypes = propTypes;
MoneyRequestAmountPage.defaultProps = defaultProps;

export default compose(
withLocalize,
withOnyx({
iou: {key: ONYXKEYS.IOU},
}),
)(IOUAmountPage);
)(MoneyRequestAmountPage);
Loading