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 android copy paste menu doens't show up in compose box #7717

Closed
wants to merge 5 commits into from

Conversation

K4tsuki
Copy link
Contributor

@K4tsuki K4tsuki commented Feb 12, 2022

Details

Fix copy paste context menu won't show up in android.
Change TextInput of compose box into uncontrolled TextInput that will improve performance of TextInput

Fixed Issues

$ #6876

Tests

  1. Type something in compose box in Android
  2. Select/highlight part of text
  3. Verify that Copy / Paste menu context show up in Android
  4. Set selection/cursor at certain part of text
  5. Input an emoji
  6. Verify emoji placement, cursor position and text change is correct after input an emoji
  • Verify that no errors appear in the JS console

QA Steps

  1. Type something in compose box in Android
  2. Select/highlight part of text
  3. Verify that Copy / Paste menu context show up in Android
  4. Set selection/cursor at certain part of text
  5. Input an emoji
  6. Verify emoji placement, cursor position and text change is correct after input an emoji
  • Verify that no errors appear in the JS console

Tested On

  • Web
  • Mobile Web
  • Desktop
  • iOS
  • Android

Screenshots

Web

web.mp4

Mobile Web

m_web.mp4

Desktop

desktop_compose.mp4

iOS

ios_compose.mp4

Android

android_cp.mp4

@K4tsuki K4tsuki requested a review from a team as a code owner February 12, 2022 14:11
@MelvinBot MelvinBot requested review from parasharrajat and stitesExpensify and removed request for a team February 12, 2022 14:11
Comment on lines 354 to 365
if (this.comment.length === 0 && newComment.length !== 0) {
if (this.state.isCommentEmpty) {
this.setState({isCommentEmpty: false});
}
Report.setReportWithDraft(this.props.reportID.toString(), true);
}

// The draft has been deleted.
if (newComment.length === 0) {
if (!this.state.isCommentEmpty) {
this.setState({isCommentEmpty: true});
}
Copy link
Member

Choose a reason for hiding this comment

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

I didn't understand this part. Also, I would like to ask that your original solution is very different from your proposal. Why so?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because I realize updateCommnet is handler for onChangeText and put setNativePropse(text: ) in there isn't doing anything because when user type a character setNativeProps is being called even though the textInput is already changed by user type (you don't need setNativeProps there).

As for setState, each time user type a character setState is being called then ReportActionCompose is being rendered. I am guarding setState of isCommentEmpty so it is not being called unnecessarily.

https://stackoverflow.com/questions/24718709/reactjs-does-render-get-called-any-time-setstate-is-called

Here I am attaching performance improvement, the first compose box is build using developement build but it is faster than second compose box that is current production release.

ft.mp4

Copy link
Member

@parasharrajat parasharrajat Feb 12, 2022

Choose a reason for hiding this comment

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

Ok. I think we can refactor this into a small function so that it does not look confusing.

setCommentEmpty(isEmpty){
	  if(this.state.isCommentEmpty === isEmpty}{
	  	return;
	  }
	  this.setState({isCommentEmpty: isEmpty});
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All right.

* @param {Object} selection: {start: number, end: number}
*/
setTextAndSelectionOfTextInput(newText, selection) {
if (Platform.OS === 'web') {
Copy link
Member

Choose a reason for hiding this comment

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

We don't use this. Check 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.

Alright, I will try other way.

Comment on lines 256 to 265
// react-native-web
// because setNativeProps in react-native-web is slow and doesn't have good support
this.textInput.value = newText;
this.textInput.setSelectionRange(selection.start, selection.end);
} else {
this.textInput.setNativeProps({text: newText, selection});

// Relasing selection handling to system
setTimeout(() => this.textInput.setNativeProps({selection: {start: undefined, end: undefined}}), 0);
}
Copy link
Member

Choose a reason for hiding this comment

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

Let's discuss this first. I have a couple of questions.

  1. why because setNativeProps in react-native-web is slow and doesn't have good support? Any reasons to support your comment.
  2. Use of setTimeout. Why is this needed?

Copy link
Contributor Author

@K4tsuki K4tsuki Feb 12, 2022

Choose a reason for hiding this comment

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

It is setTimeout 0, you can read more about it here:
https://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful
For this code it is to ensure that the previous setNativeProps is completed/done then do second setNativeProps.

As for setNativeProps for react-native-web, please wait.

Copy link
Member

Choose a reason for hiding this comment

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

And this is very confusing that you are first settings the selection and then releasing it.

Copy link
Contributor Author

@K4tsuki K4tsuki Feb 12, 2022

Choose a reason for hiding this comment

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

If we don't do that, the selection will stay or come back at that position. Maybe will adds some comments.

Copy link
Member

@parasharrajat parasharrajat left a comment

Choose a reason for hiding this comment

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

There is an issue with IOS. Where after adding the emoji the cursor is moved to the start of the text.

Please test it on IOS and Destop and add videos for those.

@K4tsuki
Copy link
Contributor Author

K4tsuki commented Feb 18, 2022

Add dekstop and ios videos, and new commit to fix IOS issue.

@K4tsuki
Copy link
Contributor Author

K4tsuki commented Feb 19, 2022

I am able to find another solution for IOS without second setNativeProps and without setTimeout / Promise resolve.

@K4tsuki
Copy link
Contributor Author

K4tsuki commented Feb 19, 2022

@stitesExpensify for update, the setTimeout 0 could be replaced with Promise/resolve.

@parasharrajat
Copy link
Member

I have raised some concerns on the slack. I will revisit this in some days.

* @param {String} text
* @param {Object} selection: {start: number, end: number}
*/
function setTextAndSelection(textInputElement, text, selection) {
Copy link
Member

Choose a reason for hiding this comment

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

@stitesExpensify This function is implemented differently on Android|IOS|Web. What do you think about this imlementation?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think I'm missing some context. Why does this need to be implemented in 3 different ways @K4tsuki ?

Copy link
Contributor Author

@K4tsuki K4tsuki Feb 26, 2022

Choose a reason for hiding this comment

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

@stitesExpensify for the web, setNativeProps for selection doesn't work, because to set the selection of text input in web the property name is selectionStart and selectionEnd. So it has different name than native selection property.

For IOS, at first I am using android code, but there is a bug, like what @parasharrajat mention above.

There is an issue with IOS. Where after adding the emoji the cursor is moved to the start of the text.

The native behavior of setting selection using setNativeProps is different in IOS and android, so I am just adjusting the code based on native behavior of android and IOS.

Also the setTimeouts can be replaced by Promise-resolve.

Copy link
Member

Choose a reason for hiding this comment

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

@stitesExpensify Hope you had a nice vacation. Please share your thoughts on the PR. I have raised some review comments that need your attention. Thanks.

Copy link
Member

@parasharrajat parasharrajat left a comment

Choose a reason for hiding this comment

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

Issue (Web): NumberLines is not updated which means the necessary events are not fired when we update the value via setTextAndSelection.

Steps:

  1. Write a multiline comment for about 6 lines.
  2. Select the text on lines 3-5.
  3. add an emoji.
  4. Notice that emoji is placed fine but there are extra lines on the text input.
  5. But when we start typing the lines are recalc.

Lines should update when we update the value on input.

Copy link
Member

@parasharrajat parasharrajat left a comment

Choose a reason for hiding this comment

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

Ok. The solution started to become messy and hacky. I will try to do some digging first. But We are just trying to patch in code to fix issues that are going to introduce more issues.

I would not suggest going too deep with platform-specific code.
As events are not firing, it means that we are doing it all wrong. Although we can put in all the vanilla js in react we should avoid it. Instead, do things react way.

Let me know if you have other ideas as well. I would hear them out first.

@K4tsuki
Copy link
Contributor Author

K4tsuki commented Mar 8, 2022

@parasharrajat What is the ideal solution? Should I don't use platform specific code at all for this issue? and Should I don't use setNativeProps too?
The TextInputFocusable is using platform specific code, only in web version of TextInputFocusable it modifies numberOfLines of TextInput.
Should I move the TextInputUtils.setTextAndSelection logic into this component instead? So I just continuing what already exist of platform specific code and I don't create new platform specific code.
and what are prohibited codes / functions that I must avoid when making PR?

@K4tsuki
Copy link
Contributor Author

K4tsuki commented Mar 9, 2022

@stitesExpensify What is the ideal solution here? Could I use my code inside TextInputFocusable, because the component have platform specific code, so I am not making new platform specific code, but I will use the existing component.

Also, Are setTimeout 0 and platform specific code strictly prohibited here?
Could you give me some advice please? thank you.

@parasharrajat
Copy link
Member

parasharrajat commented Mar 9, 2022

I will give it a shot and see if something else can be done here. It is fine to use platform-specific code but this should only be used when there are no other choices. setNativeProps is necessary to use. If you remember the whole problem is that the menu does not show due to controlled prop.

Apart from it, the problem is not that we are using platform-specific code instead of the way it is used.

@stitesExpensify
Copy link
Contributor

Closing this since K4tsuki is no longer a contributor

@github-actions
Copy link
Contributor

github-actions bot commented Apr 5, 2022

CLA Assistant Lite bot:
Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format.


I have read the CLA Document and I hereby sign the CLA


Katsuki seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You can retrigger this bot by commenting recheck in this Pull Request

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants