From 884d4cfc529a2a21829ad3fe3b37c4156075f14e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Wed, 17 Jul 2024 16:34:19 +0200 Subject: [PATCH 1/4] react native patch: add clear command that bypasses the event count --- ...act-native+0.73.4+018+textInputClear.patch | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 patches/react-native+0.73.4+018+textInputClear.patch diff --git a/patches/react-native+0.73.4+018+textInputClear.patch b/patches/react-native+0.73.4+018+textInputClear.patch new file mode 100644 index 000000000000..1cadce6a0783 --- /dev/null +++ b/patches/react-native+0.73.4+018+textInputClear.patch @@ -0,0 +1,66 @@ +diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +index 7ce04da..123968f 100644 +--- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm ++++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm +@@ -452,6 +452,12 @@ - (void)blur + [_backedTextInputView resignFirstResponder]; + } + ++- (void)clear ++{ ++ [self setTextAndSelection:_mostRecentEventCount value:@"" start:0 end:0]; ++ _mostRecentEventCount++; ++} ++ + - (void)setTextAndSelection:(NSInteger)eventCount + value:(NSString *__nullable)value + start:(NSInteger)start +diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputNativeCommands.h b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputNativeCommands.h +index fe3376a..6a9a45f 100644 +--- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputNativeCommands.h ++++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputNativeCommands.h +@@ -14,6 +14,7 @@ NS_ASSUME_NONNULL_BEGIN + @protocol RCTTextInputViewProtocol + - (void)focus; + - (void)blur; ++- (void)clear; + - (void)setTextAndSelection:(NSInteger)eventCount + value:(NSString *__nullable)value + start:(NSInteger)start +@@ -49,6 +50,19 @@ RCTTextInputHandleCommand(id componentView, const NSSt + return; + } + ++ if ([commandName isEqualToString:@"clear"]) { ++#if RCT_DEBUG ++ if ([args count] != 0) { ++ RCTLogError( ++ @"%@ command %@ received %d arguments, expected %d.", @"TextInput", commandName, (int)[args count], 0); ++ return; ++ } ++#endif ++ ++ [componentView clear]; ++ return; ++ } ++ + if ([commandName isEqualToString:@"setTextAndSelection"]) { + #if RCT_DEBUG + if ([args count] != 4) { +diff --git a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +index 8496a7d..e6bcfc4 100644 +--- a/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java ++++ b/node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java +@@ -331,6 +331,12 @@ public class ReactTextInputManager extends BaseViewManager Date: Wed, 17 Jul 2024 16:34:41 +0200 Subject: [PATCH 2/4] dispatch command on the UI thread --- .../home/report/ReportActionCompose/ReportActionCompose.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 6ff163f6ec37..1ba7e5c6f55b 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -4,7 +4,7 @@ import type {MeasureInWindowOnSuccessCallback, NativeSyntheticEvent, TextInputFo import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import {runOnJS, setNativeProps, useAnimatedRef} from 'react-native-reanimated'; +import {dispatchCommand, runOnJS, useAnimatedRef} from 'react-native-reanimated'; import type {Emoji} from '@assets/emojis/types'; import type {FileObject} from '@components/AttachmentModal'; import AttachmentModal from '@components/AttachmentModal'; @@ -366,7 +366,8 @@ function ReportActionCompose({ // We are setting the isCommentEmpty flag to true so the status of it will be in sync of the native text input state runOnJS(setIsCommentEmpty)(true); runOnJS(resetFullComposerSize)(); - setNativeProps(animatedRef, {text: ''}); // clears native text input on the UI thread + // TODO: does this work on web? + dispatchCommand(animatedRef, "clear") // clears native text input on the UI thread runOnJS(submitForm)(); }, [isSendDisabled, resetFullComposerSize, submitForm, animatedRef, isReportReadyForDisplay]); From 469cb3e55b3acf4c6ec34b2413acf760a15ff6d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Wed, 17 Jul 2024 16:55:26 +0200 Subject: [PATCH 3/4] add web/native impl --- src/libs/ComponentUtils/index.native.ts | 15 ++++++++++++++- src/libs/ComponentUtils/index.ts | 11 ++++++++++- .../ReportActionCompose/ReportActionCompose.tsx | 6 +++--- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/libs/ComponentUtils/index.native.ts b/src/libs/ComponentUtils/index.native.ts index 5ad39162e1a0..499fb5a46cf0 100644 --- a/src/libs/ComponentUtils/index.native.ts +++ b/src/libs/ComponentUtils/index.native.ts @@ -1,7 +1,20 @@ +import type { AnimatedRef} from 'react-native-reanimated'; +import { dispatchCommand } from 'react-native-reanimated'; +import type { Component } from 'react'; import type {AccessibilityRoleForm, NewPasswordAutocompleteType, PasswordAutocompleteType} from './types'; const PASSWORD_AUTOCOMPLETE_TYPE: PasswordAutocompleteType = 'password'; const NEW_PASSWORD_AUTOCOMPLETE_TYPE: NewPasswordAutocompleteType = 'password-new'; const ACCESSIBILITY_ROLE_FORM: AccessibilityRoleForm = 'none'; -export {PASSWORD_AUTOCOMPLETE_TYPE, ACCESSIBILITY_ROLE_FORM, NEW_PASSWORD_AUTOCOMPLETE_TYPE}; +/** + * Clears a text input on the UI thread using a custom clear command + * that bypasses the event count check. + */ +function forceClearInput(animatedInputRef: AnimatedRef) { + "worklet" + + dispatchCommand(animatedInputRef, 'clear'); +} + +export {PASSWORD_AUTOCOMPLETE_TYPE, ACCESSIBILITY_ROLE_FORM, NEW_PASSWORD_AUTOCOMPLETE_TYPE, forceClearInput}; diff --git a/src/libs/ComponentUtils/index.ts b/src/libs/ComponentUtils/index.ts index 38abb98594da..2ca3a258e14a 100644 --- a/src/libs/ComponentUtils/index.ts +++ b/src/libs/ComponentUtils/index.ts @@ -1,3 +1,6 @@ +import type { AnimatedRef} from 'react-native-reanimated'; +import { setNativeProps } from 'react-native-reanimated'; +import type { Component } from 'react'; import type {AccessibilityRoleForm, NewPasswordAutocompleteType, PasswordAutocompleteType} from './types'; /** @@ -7,4 +10,10 @@ const PASSWORD_AUTOCOMPLETE_TYPE: PasswordAutocompleteType = 'current-password'; const NEW_PASSWORD_AUTOCOMPLETE_TYPE: NewPasswordAutocompleteType = 'new-password'; const ACCESSIBILITY_ROLE_FORM: AccessibilityRoleForm = 'form'; -export {PASSWORD_AUTOCOMPLETE_TYPE, ACCESSIBILITY_ROLE_FORM, NEW_PASSWORD_AUTOCOMPLETE_TYPE}; +function forceClearInput(animatedInputRef: AnimatedRef) { + "worklet" + + setNativeProps(animatedInputRef, {text: ''}); +} + +export {PASSWORD_AUTOCOMPLETE_TYPE, ACCESSIBILITY_ROLE_FORM, NEW_PASSWORD_AUTOCOMPLETE_TYPE,forceClearInput}; diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index 1ba7e5c6f55b..f20f07f991bb 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -4,7 +4,7 @@ import type {MeasureInWindowOnSuccessCallback, NativeSyntheticEvent, TextInputFo import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; -import {dispatchCommand, runOnJS, useAnimatedRef} from 'react-native-reanimated'; +import {runOnJS, useAnimatedRef} from 'react-native-reanimated'; import type {Emoji} from '@assets/emojis/types'; import type {FileObject} from '@components/AttachmentModal'; import AttachmentModal from '@components/AttachmentModal'; @@ -40,6 +40,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; +import { forceClearInput } from '@libs/ComponentUtils'; import AttachmentPickerWithMenuItems from './AttachmentPickerWithMenuItems'; import ComposerWithSuggestions from './ComposerWithSuggestions'; import type {ComposerWithSuggestionsProps} from './ComposerWithSuggestions/ComposerWithSuggestions'; @@ -366,8 +367,7 @@ function ReportActionCompose({ // We are setting the isCommentEmpty flag to true so the status of it will be in sync of the native text input state runOnJS(setIsCommentEmpty)(true); runOnJS(resetFullComposerSize)(); - // TODO: does this work on web? - dispatchCommand(animatedRef, "clear") // clears native text input on the UI thread + forceClearInput(animatedRef) runOnJS(submitForm)(); }, [isSendDisabled, resetFullComposerSize, submitForm, animatedRef, isReportReadyForDisplay]); From 41d67c9f5e4290450fb5ff00d198cffd5977081a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Wed, 17 Jul 2024 17:10:25 +0200 Subject: [PATCH 4/4] prettier --- src/libs/ComponentUtils/index.native.ts | 8 ++++---- src/libs/ComponentUtils/index.ts | 10 +++++----- .../report/ReportActionCompose/ReportActionCompose.tsx | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libs/ComponentUtils/index.native.ts b/src/libs/ComponentUtils/index.native.ts index 499fb5a46cf0..7a3492c20ded 100644 --- a/src/libs/ComponentUtils/index.native.ts +++ b/src/libs/ComponentUtils/index.native.ts @@ -1,6 +1,6 @@ -import type { AnimatedRef} from 'react-native-reanimated'; -import { dispatchCommand } from 'react-native-reanimated'; -import type { Component } from 'react'; +import type {Component} from 'react'; +import type {AnimatedRef} from 'react-native-reanimated'; +import {dispatchCommand} from 'react-native-reanimated'; import type {AccessibilityRoleForm, NewPasswordAutocompleteType, PasswordAutocompleteType} from './types'; const PASSWORD_AUTOCOMPLETE_TYPE: PasswordAutocompleteType = 'password'; @@ -12,7 +12,7 @@ const ACCESSIBILITY_ROLE_FORM: AccessibilityRoleForm = 'none'; * that bypasses the event count check. */ function forceClearInput(animatedInputRef: AnimatedRef) { - "worklet" + 'worklet'; dispatchCommand(animatedInputRef, 'clear'); } diff --git a/src/libs/ComponentUtils/index.ts b/src/libs/ComponentUtils/index.ts index 2ca3a258e14a..38179d0fe361 100644 --- a/src/libs/ComponentUtils/index.ts +++ b/src/libs/ComponentUtils/index.ts @@ -1,6 +1,6 @@ -import type { AnimatedRef} from 'react-native-reanimated'; -import { setNativeProps } from 'react-native-reanimated'; -import type { Component } from 'react'; +import type {Component} from 'react'; +import type {AnimatedRef} from 'react-native-reanimated'; +import {setNativeProps} from 'react-native-reanimated'; import type {AccessibilityRoleForm, NewPasswordAutocompleteType, PasswordAutocompleteType} from './types'; /** @@ -11,9 +11,9 @@ const NEW_PASSWORD_AUTOCOMPLETE_TYPE: NewPasswordAutocompleteType = 'new-passwor const ACCESSIBILITY_ROLE_FORM: AccessibilityRoleForm = 'form'; function forceClearInput(animatedInputRef: AnimatedRef) { - "worklet" + 'worklet'; setNativeProps(animatedInputRef, {text: ''}); } -export {PASSWORD_AUTOCOMPLETE_TYPE, ACCESSIBILITY_ROLE_FORM, NEW_PASSWORD_AUTOCOMPLETE_TYPE,forceClearInput}; +export {PASSWORD_AUTOCOMPLETE_TYPE, ACCESSIBILITY_ROLE_FORM, NEW_PASSWORD_AUTOCOMPLETE_TYPE, forceClearInput}; diff --git a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx index f20f07f991bb..479d7faddc1f 100644 --- a/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx +++ b/src/pages/home/report/ReportActionCompose/ReportActionCompose.tsx @@ -23,6 +23,7 @@ import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import canFocusInputOnScreenFocus from '@libs/canFocusInputOnScreenFocus'; +import {forceClearInput} from '@libs/ComponentUtils'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import {getDraftComment} from '@libs/DraftCommentUtils'; import getModalState from '@libs/getModalState'; @@ -40,7 +41,6 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -import { forceClearInput } from '@libs/ComponentUtils'; import AttachmentPickerWithMenuItems from './AttachmentPickerWithMenuItems'; import ComposerWithSuggestions from './ComposerWithSuggestions'; import type {ComposerWithSuggestionsProps} from './ComposerWithSuggestions/ComposerWithSuggestions'; @@ -367,7 +367,7 @@ function ReportActionCompose({ // We are setting the isCommentEmpty flag to true so the status of it will be in sync of the native text input state runOnJS(setIsCommentEmpty)(true); runOnJS(resetFullComposerSize)(); - forceClearInput(animatedRef) + forceClearInput(animatedRef); runOnJS(submitForm)(); }, [isSendDisabled, resetFullComposerSize, submitForm, animatedRef, isReportReadyForDisplay]);