react-native
react-native copied to clipboard
[IOS] Text replacement by dot after 2 spaces
Summary:
Pre-requisite:
- Ensure that the option "." Shortcut" is enabled in Settings->Keyboard.
This PR addresses several issues:
When using TextInput in an uncontrolled manner, without using the value prop, after entering a letter or word and double-tapping the spacebar, it should now correctly add a "." at the end of the letter or word. When using TextInput in an uncontrolled manner, without using the value prop, after entering certain letters for text replacement, such as "omw" or "@@" (previously created), it now produces the expected replacements.
Motivation:
From Expensify app: https://github.com/Expensify/App/issues/17153 From discussion on: https://github.com/facebook/react-native/issues/27693
Changelog:
[IOS][FIXED] - For uncontrolled TextInput: Double tapping space should add "." and Text Replacement for special characters
Test Plan:
TextInput:
"." shortcut
Pre-requisite:
-
Ensure that the option "." Shortcut" is enabled in Settings->Keyboard.
-
Using the
onChangeTextand thevalueprops to set new values:- Writing a letter: like
Ethen tapping the space 2 times, you should seeE. - Writing a word: like
Testthen tapping the space 2 times, you should seeTest.
- Writing a letter: like
Text Replacement
Pre-requisite:
-
Create a new Text Replacement inside of Settings -> Keyboard, using symbols: like
@@->[email protected] -
Using the
onChangeTextand thevalueprops to set new values:- Writing the symbol created:
@@should be selected and clicking space, the@@should be replaced by[email protected]
- Writing the symbol created:
https://github.com/facebook/react-native/assets/1676818/22941057-9ac5-49f3-b469-66880b71e596
Hi @gedu!
Thank you for your pull request and welcome to our community.
Action Required
In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.
Process
In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.
Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.
If you have received this in error or have any questions, please contact us at [email protected]. Thanks!
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!
Could you provide an integration test that demonstrates how this interacts with UIKit? This piece of code is quite complex, and already has to do quite a bit of complex state management (eg see _comingFromJS, _ignoreNextTextInputCall)
Not updating the attributed string may cause issues if the text completion causes the text input to have to grow over multiple lines.
@javache Hey, yes I can, is there something already in place for TextInput?
Unfortunately, I don't think we have any in place right now. You could look at adding them here: https://github.com/facebook/react-native/tree/main/packages/rn-tester/RNTesterIntegrationTests (but this may need some work to support the new architecture). You could also try an rn-tester-e2e test.
I will try to work on this beginning next week
I ran into some issues when updating main, next week I will start adding the test under rn-tester-e2e
Ok could set the first test, and is working, will keep adding more, and will try to add as much as I can.
Testing RewriteExample
input: test space replace
output: test_space_replace
During my testing. I observed that whenever I attempted to set a text, the second and third characters were getting cropped.
https://github.com/facebook/react-native/assets/1676818/ff1da81f-6b43-467c-bbf0-cf51a3d56520
So then I applied my fix on the Native realm (I wanted to make sure that all was working before my fix, but I couldn't make it work)
https://github.com/facebook/react-native/assets/1676818/babecaf3-5fde-4229-b14a-879e64ef3790
And it seems that it is working after running many times the tests
I updated my local and seems there were many changes and I need to find a new way to fix this issue, given that seems that isn't working now
Ok I could find the fix, seems that wasn't a big change after all, but only will work for fabric, but I'm double checking. Now I'm working on the testings
The integration tests are working on Android, but on iOS getting the text from the Input isn't working. I can add text but later I can't get it back from the Input. Looking into that
| Platform | Engine | Arch | Size (bytes) | Diff |
|---|---|---|---|---|
| android | hermes | arm64-v8a | 16,574,182 | +4 |
| android | hermes | armeabi-v7a | n/a | -- |
| android | hermes | x86 | n/a | -- |
| android | hermes | x86_64 | n/a | -- |
| android | jsc | arm64-v8a | 19,955,218 | -1 |
| android | jsc | armeabi-v7a | n/a | -- |
| android | jsc | x86 | n/a | -- |
| android | jsc | x86_64 | n/a | -- |
Base commit: e4708d661bf3d01ec857635f04a4aabf9d954a5e Branch: main
@javache I added some tests. What do you think?
@javache friendly ping
@javache did you have a chance to look at this?
In addition to fixing #27693, @gedu has confirmed this will also fix #29572 and the duplicate issue #42792. Thanks for fixing these long standing issues!
hey team - can you merge? would love to get this in my app
@javache can you take a look at this or ping someone that can help us to review this PR?
I will fix the conflict soon
Thanks!!!
On Fri, Jun 7, 2024 at 6:11 AM edug @.***> wrote:
I will fix the conflict soon
— Reply to this email directly, view it on GitHub https://github.com/facebook/react-native/pull/39385#issuecomment-2154529253, or unsubscribe https://github.com/notifications/unsubscribe-auth/AFJCPNV4IOVRCIFZSEO3FW3ZGGBO3AVCNFSM6AAAAAA4TKBPT2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCNJUGUZDSMRVGM . You are receiving this because you commented.Message ID: @.***>
Apologies for the delay here, and thank you for adding the test!!
Synchronizing state between JS and native is a really complex part of React Native and introducing new variables like _stateUpdated add additional complexity there. I'd love to see a more scoped fix with detailed explanation of how updateState ends-up becoming re-entrant.
The right fix is probably not in bailing out early inside updateState, but in whatever callback gets fired that triggers the re-entrant state update. This is already what _comingFromJS means ("delegate methods textInputDidChange and textInputDidChangeSelection will exit early"). Can we leverage that instead?
@javache I found the e2e tests were removed, I will remove them from here too: https://github.com/facebook/react-native/pull/45032
I'd love to see a more scoped fix with detailed explanation of how updateState ends-up becoming re-entrant.
Yeah, I would have to dig again to remember the exact flow. The main issue is multiple calls and flow are happen almost at the same time and sometimes in different orders (depending if the change/update is for a controlled or uncontrolled TextInput) I will look into so I can refresh my memory
@javache Looking a bit deeper, I found something in the _setAttributedString when using _textOf with value and onChangeText (controlled component). The if condition was false, so it was processing the text in another way. I saw that:
newText was
A{
NSBackgroundColor = "UIExtendedSRGBColorSpace 0 0 0 0";
NSColor = "UIExtendedSRGBColorSpace 0 0 0 1";
NSFont = "<UICTFont: 0x165124130> font-family: \".SFUI-Regular\"; font-weight: normal; font-style: normal; font-size: 13.00pt";
}
oldText
A{
NSBackgroundColor = "UIExtendedSRGBColorSpace 0 0 0 0";
NSColor = "UIExtendedSRGBColorSpace 0 0 0 1";
NSFont = "<UICTFont: 0x165124130> font-family: \".SFUI-Regular\"; font-weight: normal; font-style: normal; font-size: 13.00pt";
NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 0, TailIndent 0, FirstLineHeadIndent 0, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 4, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (null), Lists (null), BaseWritingDirection 0, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 65535 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint '(null)'";
NSShadow = "NSShadow {0, -1} color = {(null)}";
}
When not using value and onChangeText (uncontrolled component), both newText and oldText were the same.
I noticed that RCTNSAttributedStringFromAttributedStringBox was making a copy of the attributes, done by RCTNSTextAttributesFromTextAttributes. It checks if the attributes exist; if they do, it copies them. In the case of the controlled component, those attributes were missing, so nothing was copied, and when _textOf checked, it failed.
Here's what I did: After checking, all the attributes are set to default values before returning the copy:
NSMutableAttributedString *mutableAttributedString = [[NSMutableAttributedString alloc]
initWithString:string
attributes:RCTNSTextAttributesFromTextAttributes(fragment.textAttributes)];
// Apply default attributes if they're missing
NSDictionary<NSAttributedStringKey, id> *defaultAttributes = RCTDefaultTextAttributes();
for (NSAttributedStringKey key in defaultAttributes) {
if (![mutableAttributedString attribute:key atIndex:0 effectiveRange:NULL]) {
[mutableAttributedString addAttribute:key value:defaultAttributes[key] range:NSMakeRange(0, mutableAttributedString.length)];
}
}
return mutableAttributedString;
That way it works. This way I don't have to add the new flag
What do you think?
https://github.com/facebook/react-native/assets/1676818/175047d2-306c-47d3-a310-da02f642c3ab
@javache did you have time to look into the new proposal?
@javache friendly ping
Does the new proposal makes sense? Looks good?
@javache can you take a look at my new proposal, I'm not using a new flag
I'll dedicate time to fixing the conflicts and getting everything sorted