-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Move the cancel and save changes to inside the compose box #17633
Changes from 5 commits
1cece21
a9ae3f2
14e773c
be5ab37
e87637b
3dc4557
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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; |
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'; | ||||||
|
@@ -255,49 +262,71 @@ class ReportActionItemMessageEdit extends React.Component { | |||||
render() { | ||||||
const hasExceededMaxCommentLength = this.state.hasExceededMaxCommentLength; | ||||||
return ( | ||||||
<View style={styles.chatItemMessage}> | ||||||
<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, | ||||||
}} | ||||||
disabled={hasExceededMaxCommentLength} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I think we should remove this prop as this is a cancel button, and we should be able to cancel the edit regardless of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch, thanks! |
||||||
> | ||||||
{({hovered, pressed}) => ( | ||||||
<Icon src={Expensicons.Close} fill={StyleUtils.getIconFillColor(getButtonState(hovered, pressed))} /> | ||||||
)} | ||||||
</Pressable> | ||||||
</Tooltip> | ||||||
</View> | ||||||
<View | ||||||
style={[ | ||||||
styles.chatItemComposeBox, | ||||||
styles.flexRow, | ||||||
this.state.isFocused ? styles.chatItemComposeBoxFocusedColor : styles.chatItemComposeBoxColor, | ||||||
styles.flexRow, | ||||||
styles.flex1, | ||||||
styles.chatItemComposeBox, | ||||||
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.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} | ||||||
|
@@ -307,26 +336,27 @@ class ReportActionItemMessageEdit extends React.Component { | |||||
/> | ||||||
</View> | ||||||
|
||||||
<View style={styles.alignSelfEnd}> | ||||||
<Tooltip text={this.props.translate('common.saveChanges')}> | ||||||
<TouchableOpacity | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AFAIK There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Touchable Opacity was used to match exactly the send button we have in the In my opinion, changing it to Pressable would also mean changing it in ReportActionComposer which is already out of the scope of this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still think that we should change this to a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If there's going to be a follow up PR changing ReportActionComposer to use Pressable instead of TouchableOpacity then it makes more sense to me to do both at the same time. By the way, TouchableOpacity is not deprecated. On reactnative.dev they recommend to use Pressable if you want to have more control:
Also, Pressable is not the same as TouchableOpacity since the latter implements animation out of the box, Pressable doesn't have animation. |
||||||
style={[styles.chatItemSubmitButton, | ||||||
(this.state.isCommentEmpty || hasExceededMaxCommentLength) ? undefined : styles.buttonSuccess, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, I miss TypeScript. |
||||||
]} | ||||||
|
||||||
onPress={this.publishDraft} | ||||||
hitSlop={{ | ||||||
top: 3, right: 3, bottom: 3, left: 3, | ||||||
}} | ||||||
nativeID={this.saveButtonID} | ||||||
disabled={hasExceededMaxCommentLength} | ||||||
> | ||||||
<Icon src={Expensicons.Checkmark} fill={(this.state.isCommentEmpty || hasExceededMaxCommentLength) ? themeColors.icon : themeColors.textLight} /> | ||||||
terrysahaidak marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
</TouchableOpacity> | ||||||
</Tooltip> | ||||||
</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> | ||||||
|
||||||
<ExceededCommentLength comment={this.state.draft} onExceededMaxCommentLength={this.setExceededMaxCommentLength} /> | ||||||
</View> | ||||||
); | ||||||
} | ||||||
|
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -1491,6 +1491,19 @@ const styles = { | |||||||||
flex: 1, | ||||||||||
}, | ||||||||||
|
||||||||||
chatItemDraft: { | ||||||||||
display: 'flex', | ||||||||||
flexDirection: 'row', | ||||||||||
Comment on lines
+1495
to
+1496
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I only see a single There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hard-coding the same style in multiple places is an anti-pattern as it violates the DRY principle. We have a checklist item for that :
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel that it's more about the usage of |
||||||||||
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({ | ||||||||||
|
@@ -1523,7 +1536,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 | ||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid apply
null
to style.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checked the repo's code and there is the usage of:
null
,{}
, andundefined
.I don't have a preference so I'm ok with
{}
in this case.