Skip to content

Commit

Permalink
Merge pull request #17633 from margelo/feature/move-edit-message-buttons
Browse files Browse the repository at this point in the history
Move the cancel and save changes to inside the compose box
  • Loading branch information
luacmartins authored May 4, 2023
2 parents f524362 + 3dc4557 commit b6eb637
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 94 deletions.
67 changes: 46 additions & 21 deletions src/pages/home/report/ReportActionItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import * as Expensicons from '../../../components/Icon/Expensicons';
import Text from '../../../components/Text';
import DisplayNames from '../../../components/DisplayNames';
import personalDetailsPropType from '../../personalDetailsPropType';
import ReportActionItemDraft from './ReportActionItemDraft';

const propTypes = {
/** Report for this action */
Expand Down Expand Up @@ -239,15 +240,55 @@ class ReportActionItem extends Component {
<>
{children}
{hasReactions && (
<ReportActionItemReactions
reactions={reactions}
toggleReaction={this.toggleReaction}
/>
<View style={this.props.draftMessage ? styles.chatItemReactionsDraftRight : {}}>
<ReportActionItemReactions
reactions={reactions}
toggleReaction={this.toggleReaction}
/>
</View>
)}
</>
);
}

/**
* Get ReportActionItem with a proper wrapper
* @param {Boolean} hovered whether the ReportActionItem is hovered
* @param {Boolean} isWhisper whether the ReportActionItem is a whisper
* @returns {Object} report action item
*/
renderReportActionItem(hovered, isWhisper) {
const content = this.renderItemContent(hovered || this.state.isContextMenuActive);

if (this.props.draftMessage) {
return (
<ReportActionItemDraft>
{content}
</ReportActionItemDraft>
);
}

if (!this.props.displayAsGroup) {
return (
<ReportActionItemSingle
action={this.props.action}
showHeader={!this.props.draftMessage}
wrapperStyles={[styles.chatItem, isWhisper ? styles.pt1 : {}]}
shouldShowSubscriptAvatar={this.props.shouldShowSubscriptAvatar}
report={this.props.report}
>
{content}
</ReportActionItemSingle>
);
}

return (
<ReportActionItemGrouped wrapperStyles={[styles.chatItem, isWhisper ? styles.pt1 : {}]}>
{content}
</ReportActionItemGrouped>
);
}

render() {
if (this.props.action.actionName === CONST.REPORT.ACTIONS.TYPE.CREATED) {
return <ReportActionItemCreated reportID={this.props.report.reportID} />;
Expand Down Expand Up @@ -323,23 +364,7 @@ class ReportActionItem extends Component {
/>
</View>
)}
{!this.props.displayAsGroup
? (
<ReportActionItemSingle
action={this.props.action}
showHeader={!this.props.draftMessage}
wrapperStyles={[styles.chatItem, isWhisper ? styles.pt1 : {}]}
shouldShowSubscriptAvatar={this.props.shouldShowSubscriptAvatar}
report={this.props.report}
>
{this.renderItemContent(hovered || this.state.isContextMenuActive)}
</ReportActionItemSingle>
)
: (
<ReportActionItemGrouped wrapperStyles={[styles.chatItem, isWhisper ? styles.pt1 : {}]}>
{this.renderItemContent(hovered || this.state.isContextMenuActive)}
</ReportActionItemGrouped>
)}
{this.renderReportActionItem(hovered, isWhisper)}
</OfflineWithFeedback>
</View>
<MiniReportActionContextMenu
Expand Down
21 changes: 21 additions & 0 deletions src/pages/home/report/ReportActionItemDraft.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import styles from '../../../styles/styles';

const propTypes = {
/** Children view component for this action item */
children: PropTypes.node.isRequired,
};

const ReportActionItemDraft = props => (
<View style={[styles.chatItemDraft]}>
<View style={styles.flex1}>
{props.children}
</View>
</View>
);

ReportActionItemDraft.propTypes = propTypes;
ReportActionItemDraft.displayName = 'ReportActionItemDraft';
export default ReportActionItemDraft;
175 changes: 103 additions & 72 deletions src/pages/home/report/ReportActionItemMessageEdit.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
/* eslint-disable rulesdir/onyx-props-must-have-default */
import lodashGet from 'lodash/get';
import React from 'react';
import {InteractionManager, Keyboard, View} from 'react-native';
import {
InteractionManager, Keyboard, Pressable, TouchableOpacity, View,
} from 'react-native';
import PropTypes from 'prop-types';
import _ from 'underscore';
import ExpensiMark from 'expensify-common/lib/ExpensiMark';
import Str from 'expensify-common/lib/str';
import reportActionPropTypes from './reportActionPropTypes';
import styles from '../../../styles/styles';
import themeColors from '../../../styles/themes/default';
import * as StyleUtils from '../../../styles/StyleUtils';
import Composer from '../../../components/Composer';
import * as Report from '../../../libs/actions/Report';
import * as ReportScrollManager from '../../../libs/ReportScrollManager';
import toggleReportActionComposeView from '../../../libs/toggleReportActionComposeView';
import openReportActionComposeViewWhenClosingMessageEdit from '../../../libs/openReportActionComposeViewWhenClosingMessageEdit';
import Button from '../../../components/Button';
import ReportActionComposeFocusManager from '../../../libs/ReportActionComposeFocusManager';
import compose from '../../../libs/compose';
import EmojiPickerButton from '../../../components/EmojiPicker/EmojiPickerButton';
import Icon from '../../../components/Icon';
import * as Expensicons from '../../../components/Icon/Expensicons';
import Tooltip from '../../../components/Tooltip';
import * as ReportActionContextMenu from './ContextMenu/ReportActionContextMenu';
import * as ReportUtils from '../../../libs/ReportUtils';
import * as EmojiUtils from '../../../libs/EmojiUtils';
import getButtonState from '../../../libs/getButtonState';
import reportPropTypes from '../../reportPropTypes';
import ExceededCommentLength from '../../../components/ExceededCommentLength';
import CONST from '../../../CONST';
Expand Down Expand Up @@ -255,79 +262,103 @@ class ReportActionItemMessageEdit extends React.Component {
render() {
const hasExceededMaxCommentLength = this.state.hasExceededMaxCommentLength;
return (
<View style={styles.chatItemMessage}>
<View
style={[
styles.chatItemComposeBox,
styles.flexRow,
this.state.isFocused ? styles.chatItemComposeBoxFocusedColor : styles.chatItemComposeBoxColor,
hasExceededMaxCommentLength && styles.borderColorDanger,
]}
>
<Composer
multiline
ref={(el) => {
this.textInput = el;
this.props.forwardedRef(el);
}}
nativeID={this.messageEditInput}
onChangeText={this.updateDraft} // Debounced saveDraftComment
onKeyPress={this.triggerSaveOrCancel}
value={this.state.draft}
maxLines={16} // This is the same that slack has
style={[styles.textInputCompose, styles.flex4, styles.editInputComposeSpacing]}
onFocus={() => {
this.setState({isFocused: true});
ReportScrollManager.scrollToIndex({animated: true, index: this.props.index}, true);
toggleReportActionComposeView(false, this.props.isSmallScreenWidth);
}}
onBlur={(event) => {
this.setState({isFocused: false});
const relatedTargetId = lodashGet(event, 'nativeEvent.relatedTarget.id');

// Return to prevent re-render when save/cancel button is pressed which cancels the onPress event by re-rendering
if (_.contains([this.saveButtonID, this.cancelButtonID, this.emojiButtonID], relatedTargetId)) {
return;
}

if (this.messageEditInput === relatedTargetId) {
return;
}
openReportActionComposeViewWhenClosingMessageEdit(this.props.isSmallScreenWidth);
}}
selection={this.state.selection}
onSelectionChange={this.onSelectionChange}
/>
<View style={styles.editChatItemEmojiWrapper}>
<EmojiPickerButton
isDisabled={this.props.shouldDisableEmojiPicker}
onModalHide={() => InteractionManager.runAfterInteractions(() => this.textInput.focus())}
onEmojiSelected={this.addEmojiToTextBox}
nativeID={this.emojiButtonID}
/>
<>
<View style={[styles.chatItemMessage, styles.flexRow]}>
<View
style={[styles.justifyContentEnd]}
>
<Tooltip text={this.props.translate('common.cancel')}>
<Pressable
style={({hovered, pressed}) => ([styles.chatItemSubmitButton, StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed))])}
nativeID={this.cancelButtonID}
onPress={this.deleteDraft}
hitSlop={{
top: 3, right: 3, bottom: 3, left: 3,
}}
>
{({hovered, pressed}) => (
<Icon src={Expensicons.Close} fill={StyleUtils.getIconFillColor(getButtonState(hovered, pressed))} />
)}
</Pressable>
</Tooltip>
</View>
<View
style={[
this.state.isFocused ? styles.chatItemComposeBoxFocusedColor : styles.chatItemComposeBoxColor,
styles.flexRow,
styles.flex1,
styles.chatItemComposeBox,
hasExceededMaxCommentLength && styles.borderColorDanger,
]}
>
<View style={styles.textInputComposeSpacing}>
<Composer
multiline
ref={(el) => {
this.textInput = el;
this.props.forwardedRef(el);
}}
nativeID={this.messageEditInput}
onChangeText={this.updateDraft} // Debounced saveDraftComment
onKeyPress={this.triggerSaveOrCancel}
value={this.state.draft}
maxLines={16} // This is the same that slack has
style={[styles.textInputCompose, styles.flex1, styles.bgTransparent]}
onFocus={() => {
this.setState({isFocused: true});
ReportScrollManager.scrollToIndex({animated: true, index: this.props.index}, true);
toggleReportActionComposeView(false, this.props.isSmallScreenWidth);
}}
onBlur={(event) => {
this.setState({isFocused: false});
const relatedTargetId = lodashGet(event, 'nativeEvent.relatedTarget.id');

// Return to prevent re-render when save/cancel button is pressed which cancels the onPress event by re-rendering
if (_.contains([this.saveButtonID, this.cancelButtonID, this.emojiButtonID], relatedTargetId)) {
return;
}

if (this.messageEditInput === relatedTargetId) {
return;
}
openReportActionComposeViewWhenClosingMessageEdit(this.props.isSmallScreenWidth);
}}
selection={this.state.selection}
onSelectionChange={this.onSelectionChange}
/>
</View>
<View style={styles.editChatItemEmojiWrapper}>
<EmojiPickerButton
isDisabled={this.props.shouldDisableEmojiPicker}
onModalHide={() => InteractionManager.runAfterInteractions(() => this.textInput.focus())}
onEmojiSelected={this.addEmojiToTextBox}
nativeID={this.emojiButtonID}
/>
</View>

<View style={styles.alignSelfEnd}>
<Tooltip text={this.props.translate('common.saveChanges')}>
<TouchableOpacity
style={[styles.chatItemSubmitButton,
hasExceededMaxCommentLength ? {} : styles.buttonSuccess,
]}

onPress={this.publishDraft}
hitSlop={{
top: 3, right: 3, bottom: 3, left: 3,
}}
nativeID={this.saveButtonID}
disabled={hasExceededMaxCommentLength}
>
<Icon src={Expensicons.Checkmark} fill={hasExceededMaxCommentLength ? themeColors.icon : themeColors.textLight} />
</TouchableOpacity>
</Tooltip>
</View>
</View>

</View>
<View style={[styles.flexRow, styles.mt1]}>
<Button
small
style={[styles.mr2]}
nativeID={this.cancelButtonID}
onPress={this.deleteDraft}
text={this.props.translate('common.cancel')}
/>
<Button
small
success
isDisabled={hasExceededMaxCommentLength}
nativeID={this.saveButtonID}
style={[styles.mr2]}
onPress={this.publishDraft}
text={this.props.translate('common.saveChanges')}
/>
<ExceededCommentLength comment={this.state.draft} onExceededMaxCommentLength={this.setExceededMaxCommentLength} />
</View>
</View>
<ExceededCommentLength comment={this.state.draft} onExceededMaxCommentLength={this.setExceededMaxCommentLength} />
</>
);
}
}
Expand Down
15 changes: 14 additions & 1 deletion src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -1498,6 +1498,19 @@ const styles = {
flex: 1,
},

chatItemDraft: {
display: 'flex',
flexDirection: 'row',
paddingTop: 8,
paddingBottom: 8,
paddingLeft: 20,
paddingRight: 20,
},

chatItemReactionsDraftRight: {
marginLeft: 52,
},

// Be extremely careful when editing the compose styles, as it is easy to introduce regressions.
// Make sure you run the following tests against any changes: #12669
textInputCompose: addOutlineWidth({
Expand Down Expand Up @@ -1530,7 +1543,7 @@ const styles = {

editInputComposeSpacing: {
backgroundColor: themeColors.transparent,
marginVertical: 6,
marginVertical: 8,
},

// composer padding should not be modified unless thoroughly tested against the cases in this PR: #12669
Expand Down

0 comments on commit b6eb637

Please sign in to comment.