Skip to content

Commit

Permalink
Merge pull request #15501 from redstar504/Brayden-MarkdownLength
Browse files Browse the repository at this point in the history
  • Loading branch information
francoisl authored Feb 28, 2023
2 parents 05c84c8 + 626f81f commit 5674cb1
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 24 deletions.
1 change: 1 addition & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ const CONST = {
SHOW_LOADING_SPINNER_DEBOUNCE_TIME: 250,
TOOLTIP_SENSE: 1000,
TRIE_INITIALIZATION: 'trie_initialization',
COMMENT_LENGTH_DEBOUNCE_TIME: 500,
},
PRIORITY_MODE: {
GSD: 'gsd',
Expand Down
61 changes: 48 additions & 13 deletions src/components/ExceededCommentLength.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,62 @@
import React from 'react';
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {debounce} from 'lodash';
import CONST from '../CONST';
import * as ReportUtils from '../libs/ReportUtils';
import Text from './Text';
import styles from '../styles/styles';

const propTypes = {
/** The current length of the comment */
commentLength: PropTypes.number.isRequired,
/** Text Comment */
comment: PropTypes.string.isRequired,

/** Update UI on parent when comment length is exceeded */
onExceededMaxCommentLength: PropTypes.func.isRequired,
};

const ExceededCommentLength = (props) => {
if (props.commentLength <= CONST.MAX_COMMENT_LENGTH) {
return null;
class ExceededCommentLength extends PureComponent {
constructor(props) {
super(props);

this.state = {
commentLength: 0,
};

// By debouncing, we defer the calculation until there is a break in typing
this.updateCommentLength = debounce(this.updateCommentLength.bind(this), CONST.TIMING.COMMENT_LENGTH_DEBOUNCE_TIME);
}

return (
<Text style={[styles.textMicro, styles.textDanger, styles.chatItemComposeSecondaryRow, styles.mlAuto, styles.pl2]}>
{`${props.commentLength}/${CONST.MAX_COMMENT_LENGTH}`}
</Text>
);
};
componentDidMount() {
this.updateCommentLength();
}

componentDidUpdate(prevProps) {
if (prevProps.comment === this.props.comment) {
return;
}

this.updateCommentLength();
}

updateCommentLength() {
const commentLength = ReportUtils.getCommentLength(this.props.comment);
this.setState({commentLength});
this.props.onExceededMaxCommentLength(commentLength > CONST.MAX_COMMENT_LENGTH);
}

render() {
if (this.state.commentLength <= CONST.MAX_COMMENT_LENGTH) {
return null;
}

return (
<Text style={[styles.textMicro, styles.textDanger, styles.chatItemComposeSecondaryRow, styles.mlAuto, styles.pl2]}>
{`${this.state.commentLength}/${CONST.MAX_COMMENT_LENGTH}`}
</Text>
);
}
}

ExceededCommentLength.propTypes = propTypes;
ExceededCommentLength.displayName = 'ExceededCommentLength';

export default ExceededCommentLength;
19 changes: 14 additions & 5 deletions src/libs/ReportUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,15 @@ function hasReportNameError(report) {
return !_.isEmpty(lodashGet(report, 'errorFields.reportName', {}));
}

/**
* @param {String} text
* @returns {String}
*/
function getParsedComment(text) {
const parser = new ExpensiMark();
return text.length < CONST.MAX_MARKUP_LENGTH ? parser.replace(text) : text;
}

/**
* @param {String} [text]
* @param {File} [file]
Expand All @@ -783,7 +792,7 @@ function buildOptimisticAddCommentReportAction(text, file) {
// For comments shorter than 10k chars, convert the comment from MD into HTML because that's how it is stored in the database
// For longer comments, skip parsing and display plaintext for performance reasons. It takes over 40s to parse a 100k long string!!
const parser = new ExpensiMark();
const commentText = text.length < CONST.MAX_MARKUP_LENGTH ? parser.replace(text) : text;
const commentText = getParsedComment(text);
const isAttachment = _.isEmpty(text) && file !== undefined;
const attachmentInfo = isAttachment ? file : {};
const htmlForNewComment = isAttachment ? 'Uploading Attachment...' : commentText;
Expand Down Expand Up @@ -1424,13 +1433,13 @@ function getNewMarkerReportActionID(report, sortedAndFilteredReportActions) {
}

/**
* Replace code points > 127 with C escape sequences, and return the resulting string's overall length
* Used for compatibility with the backend auth validator for AddComment
* Performs the markdown conversion, and replaces code points > 127 with C escape sequences
* Used for compatibility with the backend auth validator for AddComment, and to account for MD in comments
* @param {String} textComment
* @returns {Number}
* @returns {Number} The comment's total length as seen from the backend
*/
function getCommentLength(textComment) {
return textComment.replace(/[^ -~]/g, '\\u????').length;
return getParsedComment(textComment).replace(/[^ -~]/g, '\\u????').trim().length;
}

/**
Expand Down
17 changes: 14 additions & 3 deletions src/pages/home/report/ReportActionCompose.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class ReportActionCompose extends React.Component {
this.getInputPlaceholder = this.getInputPlaceholder.bind(this);
this.getIOUOptions = this.getIOUOptions.bind(this);
this.addAttachment = this.addAttachment.bind(this);
this.setExceededMaxCommentLength = this.setExceededMaxCommentLength.bind(this);
this.comment = props.comment;

// React Native will retain focus on an input for native devices but web/mWeb behave differently so we have some focus management
Expand All @@ -153,6 +154,7 @@ class ReportActionCompose extends React.Component {

// If we are on a small width device then don't show last 3 items from conciergePlaceholderOptions
conciergePlaceholderRandomIndex: _.random(this.props.translate('reportActionCompose.conciergePlaceholderOptions').length - (this.props.isSmallScreenWidth ? 4 : 1)),
hasExceededMaxCommentLength: false,
};
}

Expand Down Expand Up @@ -302,6 +304,16 @@ class ReportActionCompose extends React.Component {
this.setState({maxLines});
}

/**
* Updates the composer when the comment length is exceeded
* Shows red borders and prevents the comment from being sent
*
* @param {Boolean} hasExceededMaxCommentLength
*/
setExceededMaxCommentLength(hasExceededMaxCommentLength) {
this.setState({hasExceededMaxCommentLength});
}

isEmptyChat() {
return _.size(this.props.reportActions) === 1;
}
Expand Down Expand Up @@ -513,8 +525,7 @@ class ReportActionCompose extends React.Component {
const isComposeDisabled = this.props.isDrawerOpen && this.props.isSmallScreenWidth;
const isBlockedFromConcierge = ReportUtils.chatIncludesConcierge(this.props.report) && User.isBlockedFromConcierge(this.props.blockedFromConcierge);
const inputPlaceholder = this.getInputPlaceholder();
const encodedCommentLength = ReportUtils.getCommentLength(this.comment);
const hasExceededMaxCommentLength = encodedCommentLength > CONST.MAX_COMMENT_LENGTH;
const hasExceededMaxCommentLength = this.state.hasExceededMaxCommentLength;

return (
<View style={[
Expand Down Expand Up @@ -709,7 +720,7 @@ class ReportActionCompose extends React.Component {
>
{!this.props.isSmallScreenWidth && <OfflineIndicator containerStyles={[styles.chatItemComposeSecondaryRow]} />}
<ReportTypingIndicator reportID={this.props.reportID} />
<ExceededCommentLength commentLength={encodedCommentLength} />
<ExceededCommentLength comment={this.comment} onExceededMaxCommentLength={this.setExceededMaxCommentLength} />
</View>
{this.state.isDraggingOver && <ReportDropUI />}
</View>
Expand Down
17 changes: 14 additions & 3 deletions src/pages/home/report/ReportActionItemMessageEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class ReportActionItemMessageEdit extends React.Component {
this.triggerSaveOrCancel = this.triggerSaveOrCancel.bind(this);
this.onSelectionChange = this.onSelectionChange.bind(this);
this.addEmojiToTextBox = this.addEmojiToTextBox.bind(this);
this.setExceededMaxCommentLength = this.setExceededMaxCommentLength.bind(this);
this.saveButtonID = 'saveButton';
this.cancelButtonID = 'cancelButton';
this.emojiButtonID = 'emojiButton';
Expand All @@ -84,6 +85,7 @@ class ReportActionItemMessageEdit extends React.Component {
end: draftMessage.length,
},
isFocused: false,
hasExceededMaxCommentLength: false,
};
}

Expand All @@ -96,6 +98,16 @@ class ReportActionItemMessageEdit extends React.Component {
this.setState({selection: e.nativeEvent.selection});
}

/**
* Updates the composer when the comment length is exceeded
* Shows red borders and prevents the comment from being sent
*
* @param {Boolean} hasExceededMaxCommentLength
*/
setExceededMaxCommentLength(hasExceededMaxCommentLength) {
this.setState({hasExceededMaxCommentLength});
}

/**
* Update the value of the draft in Onyx
*
Expand Down Expand Up @@ -217,8 +229,7 @@ class ReportActionItemMessageEdit extends React.Component {
}

render() {
const draftLength = ReportUtils.getCommentLength(this.state.draft);
const hasExceededMaxCommentLength = draftLength > CONST.MAX_COMMENT_LENGTH;
const hasExceededMaxCommentLength = this.state.hasExceededMaxCommentLength;
return (
<View style={styles.chatItemMessage}>
<View
Expand Down Expand Up @@ -290,7 +301,7 @@ class ReportActionItemMessageEdit extends React.Component {
onPress={this.publishDraft}
text={this.props.translate('common.saveChanges')}
/>
<ExceededCommentLength commentLength={draftLength} />
<ExceededCommentLength comment={this.state.draft} onExceededMaxCommentLength={this.setExceededMaxCommentLength} />
</View>
</View>
);
Expand Down

0 comments on commit 5674cb1

Please sign in to comment.