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

Fix compose box resurfacing issue #9060

Merged
merged 18 commits into from
Jun 7, 2022
5 changes: 5 additions & 0 deletions src/components/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ const propTypes = {

/** Whether Button is on active screen */
isFocused: PropTypes.bool.isRequired,

/** Id to use for this button */
nativeID: PropTypes.string,
};

const defaultProps = {
Expand Down Expand Up @@ -133,6 +136,7 @@ const defaultProps = {
shouldRemoveRightBorderRadius: false,
shouldRemoveLeftBorderRadius: false,
shouldEnableHapticFeedback: false,
nativeID: '',
};

class Button extends Component {
Expand Down Expand Up @@ -247,6 +251,7 @@ class Button extends Component {
this.props.isDisabled ? {...styles.cursorDisabled, ...styles.noSelect} : {},
...this.additionalStyles,
]}
nativeID={this.props.nativeID}
>
{({pressed, hovered}) => (
<OpacityView
Expand Down
3 changes: 2 additions & 1 deletion src/libs/EmojiUtils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import _ from 'underscore';
import lodashOrderBy from 'lodash/orderBy';
import moment from 'moment';
import Str from 'expensify-common/lib/str';
import CONST from '../CONST';
import * as User from './actions/User';

Expand Down Expand Up @@ -77,7 +78,7 @@ function isSingleEmoji(message) {
* @returns {Boolean}
*/
function containsOnlyEmojis(message) {
const trimmedMessage = message.replace(/ /g, '').replaceAll('\n', '');
const trimmedMessage = Str.replaceAll(message.replace(/ /g, ''), '\n', '');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NAB; If .replaceAll is a native JS function (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replaceAll) why do we prefer using a lib for it here?

Also, .replace(/ /g, '') is basically another .replaceAll(message, ' ', '') so why not replace that one too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's poly-filled and not available on some mobile browsers

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool ya makes sense

Any thoughts on my second comment?

Also, .replace(/ /g, '') is basically another .replaceAll(message, ' ', '') so why not replace that one too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly added this to fix a bad crash with iOS 14 so that wasn't my priority. We could change the usages, but that doesn't stop people from making this mistake in the future. I also added an eslint rule to prevent people from using string.prototype.replaceAll.

const match = trimmedMessage.match(CONST.REGEX.EMOJIS);

if (!match) {
Expand Down
2 changes: 1 addition & 1 deletion src/libs/toggleReportActionComposeView/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as Session from '../actions/Session';

export default (shouldShowComposeInput, isSmallScreenWidth) => {
export default (shouldShowComposeInput, isSmallScreenWidth = true) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking out loud: index.js is for web & mobile (since we have a index.native.js file) so... should this default to false, not small screen width?

Thought about this lib name: This isn't really "toggling" anything, is it? It's just setting Session.setShouldShowComposeInput based on whatever is passed... Maybe it's due for a rename

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I don't really understand this concern. Are you seeing a bug somewhere or something that needs to be addressed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, no bug - this is related to my other 2 comments about web & desktop probably usually not being small screens, so it's weird to me that we default isSmallScreenWidth to true here.

Again, not a blocker & doesn't need to be changed until we see a bug somewhere so can resolve this

if (!isSmallScreenWidth) {
return;
}
Expand Down
4 changes: 4 additions & 0 deletions src/pages/home/ReportScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import CONST from '../../CONST';
import FullScreenLoadingIndicator from '../../components/FullscreenLoadingIndicator';
import reportActionPropTypes from './report/reportActionPropTypes';
import ArchivedReportFooter from '../../components/ArchivedReportFooter';
import toggleReportActionComposeView from '../../libs/toggleReportActionComposeView';

const propTypes = {
/** Navigation route context info provided by react navigation */
Expand Down Expand Up @@ -151,6 +152,9 @@ class ReportScreen extends React.Component {
Report.handleInaccessibleReport();
return;
}

// Always reset the state of the composer view when the current reportID changes
toggleReportActionComposeView(true);
Beamanator marked this conversation as resolved.
Show resolved Hide resolved
Report.updateCurrentlyViewedReportID(reportID);
}

Expand Down
6 changes: 6 additions & 0 deletions src/pages/home/report/ReportActionCompose.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import Tooltip from '../../../components/Tooltip';
import EmojiPickerButton from '../../../components/EmojiPicker/EmojiPickerButton';
import VirtualKeyboard from '../../../libs/VirtualKeyboard';
import canUseTouchScreen from '../../../libs/canUseTouchscreen';
import toggleReportActionComposeView from '../../../libs/toggleReportActionComposeView';
import OfflineIndicator from '../../../components/OfflineIndicator';
import ExceededCommentLength from '../../../components/ExceededCommentLength';

Expand Down Expand Up @@ -155,6 +156,11 @@ class ReportActionCompose extends React.Component {
}

componentDidUpdate(prevProps) {
const sidebarOpened = !prevProps.isDrawerOpen && this.props.isDrawerOpen;
if (sidebarOpened) {
toggleReportActionComposeView(true);
Beamanator marked this conversation as resolved.
Show resolved Hide resolved
}

// We want to focus or refocus the input when a modal has been closed and the underlying screen is focused.
// We avoid doing this on native platforms since the software keyboard popping
// open creates a jarring and broken UX.
Expand Down
4 changes: 2 additions & 2 deletions src/pages/home/report/ReportActionItemFragment.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ const ReportActionItemFragment = (props) => {
// If the only difference between fragment.text and fragment.html is <br /> tags
// we replace them with line breaks and render it as text, not as html.
// This is done to render emojis with line breaks between them as text.
const differByLineBreaksOnly = props.fragment.html.replaceAll('<br />', ' ') === props.fragment.text;
const differByLineBreaksOnly = Str.replaceAll(props.fragment.html, '<br />', ' ') === props.fragment.text;
if (differByLineBreaksOnly) {
const textWithLineBreaks = props.fragment.html.replaceAll('<br />', '\n');
const textWithLineBreaks = Str.replaceAll(props.fragment.html, '<br />', '\n');
// eslint-disable-next-line no-param-reassign
props.fragment = {...props.fragment, text: textWithLineBreaks, html: textWithLineBreaks};
}
Expand Down
14 changes: 13 additions & 1 deletion src/pages/home/report/ReportActionItemMessageEdit.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import lodashGet from 'lodash/get';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NAB: Interested in using lodashGet in some other places in this component?

Ex:

this.debouncedSaveDraft(this.props.action.message[0].html);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not in this PR unless that code is being modified already. My usual preference is to only make the changes that are necessary.

import React from 'react';
import {InteractionManager, View} from 'react-native';
import PropTypes from 'prop-types';
Expand Down Expand Up @@ -71,6 +72,8 @@ class ReportActionItemMessageEdit extends React.Component {
this.triggerSaveOrCancel = this.triggerSaveOrCancel.bind(this);
this.onSelectionChange = this.onSelectionChange.bind(this);
this.addEmojiToTextBox = this.addEmojiToTextBox.bind(this);
this.saveButtonID = 'saveButton';
this.cancelButtonID = 'cancelButton';

const parser = new ExpensiMark();
const draftMessage = parser.htmlToMarkdown(this.props.draftMessage);
Expand Down Expand Up @@ -126,7 +129,6 @@ class ReportActionItemMessageEdit extends React.Component {
* allows one to navigate somewhere else and come back to the comment and still have it in edit mode.
* @param {String} newDraft
*/

debouncedSaveDraft(newDraft) {
Report.saveReportActionDraft(this.props.reportID, this.props.action.reportActionID, newDraft);
}
Expand Down Expand Up @@ -210,6 +212,14 @@ class ReportActionItemMessageEdit extends React.Component {
ReportScrollManager.scrollToIndex({animated: true, index: this.props.index}, true);
toggleReportActionComposeView(false, VirtualKeyboard.shouldAssumeIsOpen());
}}
onBlur={(event) => {
// 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], lodashGet(event, 'nativeEvent.relatedTarget.id'))) {
return;
}

toggleReportActionComposeView(true, VirtualKeyboard.shouldAssumeIsOpen());
}}
Comment on lines +215 to +222
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marcaaron Could you please help me understand this change? It has caused an issue #9252 and I am trying to figure out what is it doing? Especially What do I need to test for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are you curious about exactly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to maybe walk me through what you think it's doing?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it is checking that if the Cancel or save button is clicked on the Editor, return, otherwise show the composer.

So do we want to show the composer on blur?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, in this case we are ignoring the blur event because when we are blurring because the cancel or save button is clicked the method to toggle the composer (toggleReportActionComposeView) would prevent the user from taking the actions. I assumed here (perhaps incorrectly) that it is related to re-rendering of the component - but the onPress method for the buttons never seemed to run. Let me know if that helps!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got, It. Thanks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to show the composer on blur?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes on blur and it should happen when we save changes or cancel the edit right? Can we have this conversation on whatever issue you are working on so I can get more insight into what you're trying to figure out?

This comment was marked as off-topic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please bring whatever conversation you'd like to have to a relevant issue.

selection={this.state.selection}
onSelectionChange={this.onSelectionChange}
/>
Expand All @@ -226,12 +236,14 @@ class ReportActionItemMessageEdit extends React.Component {
<Button
small
style={[styles.mr2]}
nativeID={this.cancelButtonID}
onPress={this.deleteDraft}
text={this.props.translate('common.cancel')}
/>
<Button
small
success
nativeID={this.saveButtonID}
style={[styles.mr2]}
onPress={this.publishDraft}
text={this.props.translate('common.saveChanges')}
Expand Down