From fe8d3b081c0511da240817576a3bf42a4010105f Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Tue, 7 Jan 2025 18:29:27 +0000 Subject: [PATCH] Fix double text input in composer (#7381) * upgrade paste input * override how backing uitextinput is created * different approach --- package.json | 2 +- ...rmost+react-native-paste-input+0.7.1.patch | 16 -- ...rmost+react-native-paste-input+0.8.1.patch | 160 ++++++++++++++++++ yarn.lock | 25 +-- 4 files changed, 170 insertions(+), 33 deletions(-) delete mode 100644 patches/@mattermost+react-native-paste-input+0.7.1.patch create mode 100644 patches/@mattermost+react-native-paste-input+0.8.1.patch diff --git a/package.json b/package.json index 4901b35604..ac11d80ca0 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "@haileyok/bluesky-video": "0.2.6", "@ipld/dag-cbor": "^9.2.0", "@lingui/react": "^4.14.1", - "@mattermost/react-native-paste-input": "^0.7.1", + "@mattermost/react-native-paste-input": "^0.8.1", "@miblanchard/react-native-slider": "^2.3.1", "@mozzius/expo-dynamic-app-icon": "^1.5.0", "@radix-ui/react-dismissable-layer": "^1.1.1", diff --git a/patches/@mattermost+react-native-paste-input+0.7.1.patch b/patches/@mattermost+react-native-paste-input+0.7.1.patch deleted file mode 100644 index dbf55b0959..0000000000 --- a/patches/@mattermost+react-native-paste-input+0.7.1.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m b/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m -index e916023..0564d97 100644 ---- a/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m -+++ b/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m -@@ -22,6 +22,11 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge - _backedTextInputView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - _backedTextInputView.textInputDelegate = self; - -+ // Disable inline predictions to prevent jank in the composer -+ if (@available(iOS 17.0, *)) { -+ _backedTextInputView.inlinePredictionType = UITextInlinePredictionTypeNo; -+ } -+ - [self addSubview:_backedTextInputView]; - } - diff --git a/patches/@mattermost+react-native-paste-input+0.8.1.patch b/patches/@mattermost+react-native-paste-input+0.8.1.patch new file mode 100644 index 0000000000..a6d1513f15 --- /dev/null +++ b/patches/@mattermost+react-native-paste-input+0.8.1.patch @@ -0,0 +1,160 @@ +diff --git a/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m b/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m +index e916023..9968f3c 100644 +--- a/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m ++++ b/node_modules/@mattermost/react-native-paste-input/ios/PasteInputView.m +@@ -3,58 +3,87 @@ + // PasteInput + // + // Created by Elias Nahum on 04-11-20. +-// Copyright © 2020 Facebook. All rights reserved. ++// Updated to remove parent’s default text view + // + + #import "PasteInputView.h" + #import "PasteInputTextView.h" +-#import ++#import // for RCTDirectEventBlock, etc. + + @implementation PasteInputView + { +- PasteInputTextView *_backedTextInputView; ++ // We'll store the custom text view in this ivar ++ PasteInputTextView *_customBackedTextView; + } + + - (instancetype)initWithBridge:(RCTBridge *)bridge + { ++ // Must call the super’s designated initializer + if (self = [super initWithBridge:bridge]) { +- _backedTextInputView = [[PasteInputTextView alloc] initWithFrame:self.bounds]; +- _backedTextInputView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; +- _backedTextInputView.textInputDelegate = self; ++ // 1. The parent (RCTMultilineTextInputView) has already created ++ // its own _backedTextInputView = [RCTUITextView new] in super init. ++ // We can remove that subview: + +- [self addSubview:_backedTextInputView]; +- } ++ id parentInputView = super.backedTextInputView; ++ if ([parentInputView isKindOfClass:[UIView class]]) { ++ UIView *parentSubview = (UIView *)parentInputView; ++ if (parentSubview.superview == self) { ++ [parentSubview removeFromSuperview]; ++ } ++ } + ++ // 2. Now create our custom PasteInputTextView ++ _customBackedTextView = [[PasteInputTextView alloc] initWithFrame:self.bounds]; ++ _customBackedTextView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; ++ _customBackedTextView.textInputDelegate = self; ++ ++ // Optional: disable inline predictions for iOS 17+ ++ if (@available(iOS 17.0, *)) { ++ _customBackedTextView.inlinePredictionType = UITextInlinePredictionTypeNo; ++ } ++ ++ // 3. Add your custom text view as the only subview ++ [self addSubview:_customBackedTextView]; ++ } + return self; + } + ++/** ++ * Override the parent's accessor so that anywhere in RN that calls ++ * `self.backedTextInputView` will get the custom PasteInputTextView. ++ */ + - (id)backedTextInputView + { +- return _backedTextInputView; ++ return _customBackedTextView; + } + +-- (void)setDisableCopyPaste:(BOOL)disableCopyPaste { +- _backedTextInputView.disableCopyPaste = disableCopyPaste; ++#pragma mark - Setters for React Props ++ ++- (void)setDisableCopyPaste:(BOOL)disableCopyPaste ++{ ++ _customBackedTextView.disableCopyPaste = disableCopyPaste; + } + +-- (void)setOnPaste:(RCTDirectEventBlock)onPaste { +- _backedTextInputView.onPaste = onPaste; ++- (void)setOnPaste:(RCTDirectEventBlock)onPaste ++{ ++ _customBackedTextView.onPaste = onPaste; + } + +-- (void)setSmartPunctuation:(NSString *)smartPunctuation { +- if ([smartPunctuation isEqualToString:@"enable"]) { +- [_backedTextInputView setSmartDashesType:UITextSmartDashesTypeYes]; +- [_backedTextInputView setSmartQuotesType:UITextSmartQuotesTypeYes]; +- [_backedTextInputView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeYes]; +- } else if ([smartPunctuation isEqualToString:@"disable"]) { +- [_backedTextInputView setSmartDashesType:UITextSmartDashesTypeNo]; +- [_backedTextInputView setSmartQuotesType:UITextSmartQuotesTypeNo]; +- [_backedTextInputView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeNo]; +- } else { +- [_backedTextInputView setSmartDashesType:UITextSmartDashesTypeDefault]; +- [_backedTextInputView setSmartQuotesType:UITextSmartQuotesTypeDefault]; +- [_backedTextInputView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeDefault]; +- } ++- (void)setSmartPunctuation:(NSString *)smartPunctuation ++{ ++ if ([smartPunctuation isEqualToString:@"enable"]) { ++ [_customBackedTextView setSmartDashesType:UITextSmartDashesTypeYes]; ++ [_customBackedTextView setSmartQuotesType:UITextSmartQuotesTypeYes]; ++ [_customBackedTextView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeYes]; ++ } else if ([smartPunctuation isEqualToString:@"disable"]) { ++ [_customBackedTextView setSmartDashesType:UITextSmartDashesTypeNo]; ++ [_customBackedTextView setSmartQuotesType:UITextSmartQuotesTypeNo]; ++ [_customBackedTextView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeNo]; ++ } else { ++ [_customBackedTextView setSmartDashesType:UITextSmartDashesTypeDefault]; ++ [_customBackedTextView setSmartQuotesType:UITextSmartQuotesTypeDefault]; ++ [_customBackedTextView setSmartInsertDeleteType:UITextSmartInsertDeleteTypeDefault]; ++ } + } + + #pragma mark - UIScrollViewDelegate +@@ -62,7 +91,6 @@ + - (void)scrollViewDidScroll:(UIScrollView *)scrollView + { + RCTDirectEventBlock onScroll = self.onScroll; +- + if (onScroll) { + CGPoint contentOffset = scrollView.contentOffset; + CGSize contentSize = scrollView.contentSize; +@@ -71,22 +99,22 @@ + + onScroll(@{ + @"contentOffset": @{ +- @"x": @(contentOffset.x), +- @"y": @(contentOffset.y) ++ @"x": @(contentOffset.x), ++ @"y": @(contentOffset.y) + }, + @"contentInset": @{ +- @"top": @(contentInset.top), +- @"left": @(contentInset.left), +- @"bottom": @(contentInset.bottom), +- @"right": @(contentInset.right) ++ @"top": @(contentInset.top), ++ @"left": @(contentInset.left), ++ @"bottom": @(contentInset.bottom), ++ @"right": @(contentInset.right) + }, + @"contentSize": @{ +- @"width": @(contentSize.width), +- @"height": @(contentSize.height) ++ @"width": @(contentSize.width), ++ @"height": @(contentSize.height) + }, + @"layoutMeasurement": @{ +- @"width": @(size.width), +- @"height": @(size.height) ++ @"width": @(size.width), ++ @"height": @(size.height) + }, + @"zoomScale": @(scrollView.zoomScale ?: 1), + }); diff --git a/yarn.lock b/yarn.lock index a17e875d0b..cdc028fdc2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4990,12 +4990,12 @@ "@babel/runtime" "^7.20.13" "@lingui/core" "4.14.1" -"@mattermost/react-native-paste-input@^0.7.1": - version "0.7.1" - resolved "https://registry.yarnpkg.com/@mattermost/react-native-paste-input/-/react-native-paste-input-0.7.1.tgz#f14585030b992cf7c9bbd0921225eefa501756ba" - integrity sha512-kY8LKtqRX2T/rtn/HNrzTitijuATvyzd6yl5WNWOsszmyzNcssKStjjCTBup04CyMxfwutUU1CWrYUb3hQO7oA== +"@mattermost/react-native-paste-input@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@mattermost/react-native-paste-input/-/react-native-paste-input-0.8.1.tgz#944ec69d0c49c3607265a02ad04103cc5b556caf" + integrity sha512-QHpwWORPALmX5FczCewRlJkVqtltD84mlpMpto5IGLKdHMAoHeXMiG4VJVh2XkRQvTyUhYegtrUrJzf8dkJokA== dependencies: - semver "7.6.0" + semver "7.6.3" "@messageformat/parser@^5.0.0": version "5.1.0" @@ -16903,12 +16903,10 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== -semver@7.6.0: - version "7.6.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" - integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== - dependencies: - lru-cache "^6.0.0" +semver@7.6.3, semver@^7.1.3, semver@^7.6.3: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== semver@^5.5.0, semver@^5.6.0: version "5.7.2" @@ -16920,11 +16918,6 @@ semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.1.3, semver@^7.6.3: - version "7.6.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" - integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== - semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"