Skip to content

Commit

Permalink
Merge pull request #20271 from multijump/16280_refactor_MoneyRequestP…
Browse files Browse the repository at this point in the history
…articipantsSplitSelector

Migrate MoneyRequestParticipantsSplitSelector.js to function component
  • Loading branch information
Julesssss authored Jul 4, 2023
2 parents 917c344 + b70b37e commit e23cd16
Showing 1 changed file with 93 additions and 136 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {Component} from 'react';
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import _ from 'underscore';
Expand Down Expand Up @@ -55,183 +55,140 @@ const defaultProps = {
safeAreaPaddingBottomStyle: {},
};

class MoneyRequestParticipantsSplitSelector extends Component {
constructor(props) {
super(props);

this.toggleOption = this.toggleOption.bind(this);
this.finalizeParticipants = this.finalizeParticipants.bind(this);
this.updateOptionsWithSearchTerm = this.updateOptionsWithSearchTerm.bind(this);

const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions(
props.reports,
props.personalDetails,
props.betas,
'',
props.participants,
CONST.EXPENSIFY_EMAILS,
);

this.state = {
searchTerm: '',
recentReports,
personalDetails,
userToInvite,
};
}

componentDidUpdate(prevProps) {
if (_.isEqual(prevProps.reports, this.props.reports) && _.isEqual(prevProps.personalDetails, this.props.personalDetails)) {
return;
}
this.updateOptionsWithSearchTerm(this.state.searchTerm);
}
function MoneyRequestParticipantsSplitSelector({betas, participants, personalDetails, reports, translate, onAddParticipants, onStepComplete, safeAreaPaddingBottomStyle}) {
const [searchTerm, setSearchTerm] = useState('');
const [newChatOptions, setNewChatOptions] = useState({
recentReports: [],
personalDetails: [],
userToInvite: null,
});

const maxParticipantsReached = participants.length === CONST.REPORT.MAXIMUM_PARTICIPANTS;

/**
* Returns the sections needed for the OptionsSelector
*
* @param {Boolean} maxParticipantsReached
* @returns {Array}
*/
getSections(maxParticipantsReached) {
const sections = [];
const sections = useMemo(() => {
const newSections = [];
let indexOffset = 0;

sections.push({
newSections.push({
title: undefined,
data: OptionsListUtils.getParticipantsOptions(this.props.participants, this.props.personalDetails),
data: OptionsListUtils.getParticipantsOptions(participants, personalDetails),
shouldShow: true,
indexOffset,
});
indexOffset += this.props.participants.length;
indexOffset += participants.length;

if (maxParticipantsReached) {
return sections;
return newSections;
}

sections.push({
title: this.props.translate('common.recents'),
data: this.state.recentReports,
shouldShow: !_.isEmpty(this.state.recentReports),
newSections.push({
title: translate('common.recents'),
data: newChatOptions.recentReports,
shouldShow: !_.isEmpty(newChatOptions.recentReports),
indexOffset,
});
indexOffset += this.state.recentReports.length;
indexOffset += newChatOptions.recentReports.length;

sections.push({
title: this.props.translate('common.contacts'),
data: this.state.personalDetails,
shouldShow: !_.isEmpty(this.state.personalDetails),
newSections.push({
title: translate('common.contacts'),
data: newChatOptions.personalDetails,
shouldShow: !_.isEmpty(newChatOptions.personalDetails),
indexOffset,
});
indexOffset += this.state.personalDetails.length;
indexOffset += newChatOptions.personalDetails.length;

if (this.state.userToInvite && !OptionsListUtils.isCurrentUser(this.state.userToInvite)) {
sections.push({
if (newChatOptions.userToInvite && !OptionsListUtils.isCurrentUser(newChatOptions.userToInvite)) {
newSections.push({
undefined,
data: [this.state.userToInvite],
data: [newChatOptions.userToInvite],
shouldShow: true,
indexOffset,
});
}

return sections;
}

updateOptionsWithSearchTerm(searchTerm = '') {
const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions(
this.props.reports,
this.props.personalDetails,
this.props.betas,
searchTerm,
this.props.participants,
CONST.EXPENSIFY_EMAILS,
);
this.setState({
searchTerm,
userToInvite,
recentReports,
personalDetails,
});
}

/**
* Once a single or more users are selected, navigates to next step
*/
finalizeParticipants() {
this.props.onStepComplete();
}
return newSections;
}, [maxParticipantsReached, newChatOptions, participants, personalDetails, translate]);

/**
* Removes a selected option from list if already selected. If not already selected add this option to the list.
* @param {Object} option
*/
toggleOption(option) {
const isOptionInList = _.some(this.props.participants, (selectedOption) => selectedOption.accountID === option.accountID);
const toggleOption = useCallback(
(option) => {
const isOptionInList = _.some(participants, (selectedOption) => selectedOption.accountID === option.accountID);

let newSelectedOptions;
let newSelectedOptions;

if (isOptionInList) {
newSelectedOptions = _.reject(this.props.participants, (selectedOption) => selectedOption.accountID === option.accountID);
} else {
newSelectedOptions = [...this.props.participants, {accountID: option.accountID, login: option.login, selected: true}];
}
if (isOptionInList) {
newSelectedOptions = _.reject(participants, (selectedOption) => selectedOption.accountID === option.accountID);
} else {
newSelectedOptions = [...participants, {accountID: option.accountID, login: option.login, selected: true}];
}

onAddParticipants(newSelectedOptions);

const chatOptions = OptionsListUtils.getNewChatOptions(reports, personalDetails, betas, isOptionInList ? searchTerm : '', newSelectedOptions, CONST.EXPENSIFY_EMAILS);

this.props.onAddParticipants(newSelectedOptions);

this.setState((prevState) => {
const {recentReports, personalDetails, userToInvite} = OptionsListUtils.getNewChatOptions(
this.props.reports,
this.props.personalDetails,
this.props.betas,
isOptionInList ? prevState.searchTerm : '',
newSelectedOptions,
CONST.EXPENSIFY_EMAILS,
);
return {
recentReports,
personalDetails,
userToInvite,
searchTerm: isOptionInList ? prevState.searchTerm : '',
};
setNewChatOptions({
recentReports: chatOptions.recentReports,
personalDetails: chatOptions.personalDetails,
userToInvite: chatOptions.userToInvite,
});
if (!isOptionInList) {
setSearchTerm('');
}
},
[searchTerm, participants, onAddParticipants, reports, personalDetails, betas, setNewChatOptions, setSearchTerm],
);

const headerMessage = OptionsListUtils.getHeaderMessage(
newChatOptions.personalDetails.length + newChatOptions.recentReports.length !== 0,
Boolean(newChatOptions.userToInvite),
searchTerm,
maxParticipantsReached,
);
const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(personalDetails);

useEffect(() => {
const chatOptions = OptionsListUtils.getNewChatOptions(reports, personalDetails, betas, searchTerm, participants, CONST.EXPENSIFY_EMAILS);
setNewChatOptions({
recentReports: chatOptions.recentReports,
personalDetails: chatOptions.personalDetails,
userToInvite: chatOptions.userToInvite,
});
}

render() {
const maxParticipantsReached = this.props.participants.length === CONST.REPORT.MAXIMUM_PARTICIPANTS;
const sections = this.getSections(maxParticipantsReached);
const headerMessage = OptionsListUtils.getHeaderMessage(
this.state.personalDetails.length + this.state.recentReports.length !== 0,
Boolean(this.state.userToInvite),
this.state.searchTerm,
maxParticipantsReached,
);
const isOptionsDataReady = ReportUtils.isReportDataReady() && OptionsListUtils.isPersonalDetailsReady(this.props.personalDetails);

return (
<View style={[styles.flex1, styles.w100, this.props.participants.length > 0 ? this.props.safeAreaPaddingBottomStyle : {}]}>
<OptionsSelector
canSelectMultipleOptions
sections={sections}
selectedOptions={this.props.participants}
value={this.state.searchTerm}
onSelectRow={this.toggleOption}
onChangeText={this.updateOptionsWithSearchTerm}
headerMessage={headerMessage}
boldStyle
shouldShowConfirmButton
confirmButtonText={this.props.translate('common.next')}
onConfirmSelection={this.finalizeParticipants}
textInputLabel={this.props.translate('optionsSelector.nameEmailOrPhoneNumber')}
safeAreaPaddingBottomStyle={this.props.safeAreaPaddingBottomStyle}
shouldShowOptions={isOptionsDataReady}
/>
</View>
);
}
}, [betas, reports, participants, personalDetails, translate, searchTerm, setNewChatOptions]);

return (
<View style={[styles.flex1, styles.w100, participants.length > 0 ? safeAreaPaddingBottomStyle : {}]}>
<OptionsSelector
canSelectMultipleOptions
sections={sections}
selectedOptions={participants}
value={searchTerm}
onSelectRow={toggleOption}
onChangeText={setSearchTerm}
headerMessage={headerMessage}
boldStyle
shouldShowConfirmButton
confirmButtonText={translate('common.next')}
onConfirmSelection={onStepComplete}
textInputLabel={translate('optionsSelector.nameEmailOrPhoneNumber')}
safeAreaPaddingBottomStyle={safeAreaPaddingBottomStyle}
shouldShowOptions={isOptionsDataReady}
/>
</View>
);
}

MoneyRequestParticipantsSplitSelector.propTypes = propTypes;
MoneyRequestParticipantsSplitSelector.defaultProps = defaultProps;
MoneyRequestParticipantsSplitSelector.displayName = 'MoneyRequestParticipantsSplitSelector';

export default compose(
withLocalize,
Expand Down

0 comments on commit e23cd16

Please sign in to comment.