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

refactor CreateMenu to have dynamic items #1548

Merged
merged 12 commits into from
Mar 22, 2021
12 changes: 12 additions & 0 deletions assets/images/money-circle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions assets/images/receipt.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ const CONST = {
ERROR: {
API_OFFLINE: 'API is offline',
},
MENU_ITEM_KEYS: {
NEW_CHAT: 'NewChat',
NEW_GROUP: 'NewGroup',
REQUEST_MONEY: 'RequestMoney',
SPLIT_BILL: 'SplitBill',
ATTACHMENT_PICKER: 'AttachmentPicker',
},
NVP: {
PAYPAL_ME_ADDRESS: 'expensify_payPalMeAddress',
PRIORITY_MODE: 'priorityMode',
Expand Down
79 changes: 55 additions & 24 deletions src/components/CreateMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import {View} from 'react-native';
import PropTypes from 'prop-types';
import Popover from './Popover';
import styles from '../styles/styles';
import {ChatBubble, Users} from './Icon/Expensicons';
import Navigation from '../libs/Navigation/Navigation';
import {
ChatBubble, Users, Receipt, MoneyCircle, Paperclip,
} from './Icon/Expensicons';
import ROUTES from '../ROUTES';
import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions';
import CONST from '../CONST';
Julesssss marked this conversation as resolved.
Show resolved Hide resolved
import Navigation from '../libs/Navigation/Navigation';
import MenuItem from './MenuItem';

const propTypes = {
Expand All @@ -19,16 +22,64 @@ const propTypes = {
// Callback to fire when a CreateMenu item is selected
onItemSelected: PropTypes.func.isRequired,

// Menu items to be rendered on the list
menuOptions: PropTypes.arrayOf(
PropTypes.oneOf(Object.values(CONST.MENU_ITEM_KEYS)),
).isRequired,

// Callback to fire when a AttachmentPicker item is selected
onAttachmentPickerSelected: PropTypes.func,

...windowDimensionsPropTypes,
};

const defaultProps = {
onAttachmentPickerSelected: () => {},
};

class CreateMenu extends PureComponent {
constructor(props) {
super(props);

this.setOnModalHide = this.setOnModalHide.bind(this);
this.resetOnModalHide = this.resetOnModalHide.bind(this);
this.onModalHide = () => {};

const MENU_ITEMS = {
[CONST.MENU_ITEM_KEYS.NEW_CHAT]: {
icon: ChatBubble,
text: 'New Chat',
onSelected: () => Navigation.navigate(ROUTES.NEW_CHAT),
},
[CONST.MENU_ITEM_KEYS.NEW_GROUP]: {
icon: Users,
text: 'New Group',
onSelected: () => Navigation.navigate(ROUTES.NEW_GROUP),
},
[CONST.MENU_ITEM_KEYS.REQUEST_MONEY]: {
icon: MoneyCircle,
text: 'Request Money',
onSelected: () => Navigation.navigate(ROUTES.NEW_CHAT),
},
[CONST.MENU_ITEM_KEYS.SPLIT_BILL]: {
icon: Receipt,
text: 'Split Bill',
onSelected: () => Navigation.navigate(ROUTES.NEW_CHAT),
},
[CONST.MENU_ITEM_KEYS.ATTACHMENT_PICKER]: {
icon: Paperclip,
text: 'Add Attachment',
onSelected: () => this.props.onAttachmentPickerSelected(),
Julesssss marked this conversation as resolved.
Show resolved Hide resolved
},
};

this.menuItemData = props.menuOptions.map(key => ({
...MENU_ITEMS[key],
onPress: () => {
props.onItemSelected();
this.setOnModalHide(() => MENU_ITEMS[key].onSelected());
},
}));
}

/**
Expand All @@ -47,27 +98,6 @@ class CreateMenu extends PureComponent {
}

render() {
// This format allows to set individual callbacks to each item
// while including mutual callbacks first
const menuItemData = [
{
icon: ChatBubble,
text: 'New Chat',
onPress: () => this.setOnModalHide(() => Navigation.navigate(ROUTES.NEW_CHAT)),
},
{
icon: Users,
text: 'New Group',
onPress: () => this.setOnModalHide(() => Navigation.navigate(ROUTES.NEW_GROUP)),
},
].map(item => ({
...item,
onPress: () => {
this.props.onItemSelected();
item.onPress();
},
}));

return (
<Popover
onClose={this.props.onClose}
Expand All @@ -79,7 +109,7 @@ class CreateMenu extends PureComponent {
anchorPosition={styles.createMenuPosition}
>
<View style={this.props.isSmallScreenWidth ? {} : styles.createMenuContainer}>
{menuItemData.map(({icon, text, onPress}) => (
{this.menuItemData.map(({icon, text, onPress}) => (
<MenuItem
key={text}
icon={icon}
Expand All @@ -94,5 +124,6 @@ class CreateMenu extends PureComponent {
}

CreateMenu.propTypes = propTypes;
CreateMenu.defaultProps = defaultProps;
CreateMenu.displayName = 'CreateMenu';
export default withWindowDimensions(CreateMenu);
4 changes: 4 additions & 0 deletions src/components/Icon/Expensicons.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import Send from '../../../assets/images/send.svg';
import Trashcan from '../../../assets/images/trashcan.svg';
import Users from '../../../assets/images/users.svg';
import Checkmark from '../../../assets/images/checkmark.svg';
import Receipt from '../../../assets/images/receipt.svg';
import MoneyCircle from '../../../assets/images/money-circle.svg';
import Download from '../../../assets/images/download.svg';
import DownArrow from '../../../assets/images/down.svg';
import Profile from '../../../assets/images/profile.svg';
Expand All @@ -40,6 +42,8 @@ export {
Trashcan,
Users,
Checkmark,
Receipt,
MoneyCircle,
Download,
Profile,
Gear,
Expand Down
4 changes: 1 addition & 3 deletions src/pages/home/ReportScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ const ReportScreen = (props) => {
onNavigationMenuButtonClicked={() => Navigation.navigate(ROUTES.HOME)}
/>
<View style={[styles.dFlex, styles.flex1]}>
<ReportView
reportID={activeReportID}
/>
<ReportView reportID={activeReportID} />
</View>
</ScreenWrapper>
);
Expand Down
77 changes: 57 additions & 20 deletions src/pages/home/report/ReportActionCompose.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import themeColors from '../../../styles/themes/default';
import TextInputFocusable from '../../../components/TextInputFocusable';
import ONYXKEYS from '../../../ONYXKEYS';
import Icon from '../../../components/Icon';
import {Paperclip, Send} from '../../../components/Icon/Expensicons';
import {Plus, Send} from '../../../components/Icon/Expensicons';
import AttachmentPicker from '../../../components/AttachmentPicker';
import {addAction, saveReportComment, broadcastUserIsTyping} from '../../../libs/actions/Report';
import ReportTypingIndicator from './ReportTypingIndicator';
import AttachmentModal from '../../../components/AttachmentModal';
import withWindowDimensions, {windowDimensionsPropTypes} from '../../../components/withWindowDimensions';
import compose from '../../../libs/compose';
import CreateMenu from '../../../components/CreateMenu';
import CONST from '../../../CONST';
import Navigation from '../../../libs/Navigation/Navigation';

const propTypes = {
Expand All @@ -34,6 +36,13 @@ const propTypes = {
isVisible: PropTypes.bool,
}),

// The report currently being looked at
report: PropTypes.shape({

// participants associated with current report
participants: PropTypes.arrayOf(PropTypes.string),
}).isRequired,

...windowDimensionsPropTypes,
};

Expand All @@ -45,7 +54,6 @@ const defaultProps = {
class ReportActionCompose extends React.Component {
constructor(props) {
super(props);

this.updateComment = this.updateComment.bind(this);
this.debouncedSaveReportComment = _.debounce(this.debouncedSaveReportComment.bind(this), 1000, false);
this.debouncedBroadcastUserIsTyping = _.debounce(this.debouncedBroadcastUserIsTyping.bind(this), 100, true);
Expand All @@ -59,6 +67,7 @@ class ReportActionCompose extends React.Component {
isFocused: false,
textInputShouldClear: false,
isCommentEmpty: props.comment.length === 0,
isMenuVisible: false,
};
}

Expand Down Expand Up @@ -96,6 +105,15 @@ class ReportActionCompose extends React.Component {
this.setState({textInputShouldClear: shouldClear});
}

/**
* Updates the visiblity state of the menu
*
* @param {Boolean} isMenuVisible
*/
setMenuVisibility(isMenuVisible) {
this.setState({isMenuVisible});
}

/**
* Save our report comment in Onyx. We debounce this method in the constructor so that it's not called too often
* to update Onyx and re-render this component.
Expand Down Expand Up @@ -168,6 +186,7 @@ class ReportActionCompose extends React.Component {
// focus this from the chat switcher.
// https://github.com/Expensify/Expensify.cash/issues/1228
const inputDisable = this.props.isSmallScreenWidth && Navigation.isDrawerOpen();
const hasMultipleParticipants = lodashGet(this.props.report, 'participants.length') > 1;

return (
<View style={[styles.chatItemCompose]}>
Expand All @@ -190,24 +209,39 @@ class ReportActionCompose extends React.Component {
<>
<AttachmentPicker>
{({openPicker}) => (
<TouchableOpacity
onPress={(e) => {
e.preventDefault();

// Do not open attachment picker from keypress event
if (!e.key) {
openPicker({
onPicked: (file) => {
displayFileInModal({file});
},
});
}
}}
style={[styles.chatItemAttachButton]}
underlayColor={themeColors.componentBG}
>
<Icon src={Paperclip} />
</TouchableOpacity>
<>
<TouchableOpacity
onPress={(e) => {
e.preventDefault();
this.setMenuVisibility(true);
}}
style={styles.chatItemAttachButton}
underlayColor={themeColors.componentBG}
>
<Icon src={Plus} />
</TouchableOpacity>
<CreateMenu
isVisible={this.state.isMenuVisible}
Maftalion marked this conversation as resolved.
Show resolved Hide resolved
onClose={() => this.setMenuVisibility(false)}
onAttachmentPickerSelected={() => {
setTimeout(() => {
Julesssss marked this conversation as resolved.
Show resolved Hide resolved
openPicker({
onPicked: (file) => {
displayFileInModal({file});
},
});
}, 10);
}}
onItemSelected={() => this.setMenuVisibility(false)}
menuOptions={hasMultipleParticipants
? [
CONST.MENU_ITEM_KEYS.SPLIT_BILL,
CONST.MENU_ITEM_KEYS.ATTACHMENT_PICKER]
: [
CONST.MENU_ITEM_KEYS.REQUEST_MONEY,
CONST.MENU_ITEM_KEYS.ATTACHMENT_PICKER]}
/>
</>
)}
</AttachmentPicker>
<TextInputFocusable
Expand Down Expand Up @@ -273,6 +307,9 @@ export default compose(
modal: {
key: ONYXKEYS.MODAL,
},
report: {
key: ({reportID}) => `${ONYXKEYS.COLLECTION.REPORT}${reportID}`,
},
}),
withWindowDimensions,
)(ReportActionCompose);
1 change: 0 additions & 1 deletion src/pages/home/report/ReportView.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ class ReportView extends React.Component {
<ReportActionsView
reportID={this.props.reportID}
/>

<ReportActionCompose
onSubmit={text => addAction(this.props.reportID, text)}
reportID={this.props.reportID}
Expand Down
6 changes: 6 additions & 0 deletions src/pages/home/sidebar/SidebarScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ class SidebarScreen extends Component {
onClose={this.toggleCreateMenu}
isVisible={this.state.isCreateMenuActive}
onItemSelected={this.onCreateMenuItemSelected}
menuOptions={[
CONST.MENU_ITEM_KEYS.NEW_CHAT,
CONST.MENU_ITEM_KEYS.REQUEST_MONEY,
CONST.MENU_ITEM_KEYS.NEW_GROUP,
CONST.MENU_ITEM_KEYS.SPLIT_BILL,
]}
/>
</>
)}
Expand Down