diff --git a/assets/images/attachment-not-found.svg b/assets/images/attachment-not-found.svg
index 25da973ce9cb..87231be3741b 100644
--- a/assets/images/attachment-not-found.svg
+++ b/assets/images/attachment-not-found.svg
@@ -1,18 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/binoculars.svg b/assets/images/binoculars.svg
index 64977dee38b5..1739abff408c 100644
--- a/assets/images/binoculars.svg
+++ b/assets/images/binoculars.svg
@@ -1,25 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/bolt.svg b/assets/images/bolt.svg
index 76334459107b..d2311261b5ec 100644
--- a/assets/images/bolt.svg
+++ b/assets/images/bolt.svg
@@ -1,4 +1 @@
-
-
+
\ No newline at end of file
diff --git a/assets/images/buildings.svg b/assets/images/buildings.svg
index 42171d499f26..4acce6e971be 100644
--- a/assets/images/buildings.svg
+++ b/assets/images/buildings.svg
@@ -1,12 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/companyCards/card-bofa.svg b/assets/images/companyCards/card-bofa.svg
index c58229f1b242..5b210bbe2a93 100644
--- a/assets/images/companyCards/card-bofa.svg
+++ b/assets/images/companyCards/card-bofa.svg
@@ -1,27 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/companyCards/card-capitalone.svg b/assets/images/companyCards/card-capitalone.svg
index 9f1402298683..650fe5cb53d8 100644
--- a/assets/images/companyCards/card-capitalone.svg
+++ b/assets/images/companyCards/card-capitalone.svg
@@ -1,23 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/companyCards/large/card-amex-large.svg b/assets/images/companyCards/large/card-amex-large.svg
index 06f0f57e16d2..2de2e018911a 100644
--- a/assets/images/companyCards/large/card-amex-large.svg
+++ b/assets/images/companyCards/large/card-amex-large.svg
@@ -1,32 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/companyCards/large/card-bofa-large.svg b/assets/images/companyCards/large/card-bofa-large.svg
index c83e06ffb65d..5babf9a7b611 100644
--- a/assets/images/companyCards/large/card-bofa-large.svg
+++ b/assets/images/companyCards/large/card-bofa-large.svg
@@ -1,27 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/companyCards/large/card-brex-large.svg b/assets/images/companyCards/large/card-brex-large.svg
index e1a48c3dbe39..2a3d09ee6240 100644
--- a/assets/images/companyCards/large/card-brex-large.svg
+++ b/assets/images/companyCards/large/card-brex-large.svg
@@ -1,23 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/companyCards/large/card-capital_one-large.svg b/assets/images/companyCards/large/card-capital_one-large.svg
index 20f3bd442d9e..01ea419f6a39 100644
--- a/assets/images/companyCards/large/card-capital_one-large.svg
+++ b/assets/images/companyCards/large/card-capital_one-large.svg
@@ -1,23 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/companyCards/large/card-chase-large.svg b/assets/images/companyCards/large/card-chase-large.svg
index 2b0904ae225d..2be1eb205c28 100644
--- a/assets/images/companyCards/large/card-chase-large.svg
+++ b/assets/images/companyCards/large/card-chase-large.svg
@@ -1,26 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/companyCards/large/card-citi-large.svg b/assets/images/companyCards/large/card-citi-large.svg
index 14e3ecd36850..2a7b8e55105b 100644
--- a/assets/images/companyCards/large/card-citi-large.svg
+++ b/assets/images/companyCards/large/card-citi-large.svg
@@ -1,31 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/companyCards/large/card-expensify-large.svg b/assets/images/companyCards/large/card-expensify-large.svg
index 2cef4a59ca20..3948b4718031 100644
--- a/assets/images/companyCards/large/card-expensify-large.svg
+++ b/assets/images/companyCards/large/card-expensify-large.svg
@@ -1,61 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/companyCards/large/card-generic-large.svg b/assets/images/companyCards/large/card-generic-large.svg
index 542d34fada88..0979107526e6 100644
--- a/assets/images/companyCards/large/card-generic-large.svg
+++ b/assets/images/companyCards/large/card-generic-large.svg
@@ -1,26 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/companyCards/large/card-mastercard-large.svg b/assets/images/companyCards/large/card-mastercard-large.svg
index efc27960ef73..3d21c4b7bcdc 100644
--- a/assets/images/companyCards/large/card-mastercard-large.svg
+++ b/assets/images/companyCards/large/card-mastercard-large.svg
@@ -1,33 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/companyCards/large/card-stripe-large.svg b/assets/images/companyCards/large/card-stripe-large.svg
index cd084457f5b7..61d852342b90 100644
--- a/assets/images/companyCards/large/card-stripe-large.svg
+++ b/assets/images/companyCards/large/card-stripe-large.svg
@@ -1,32 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/companyCards/large/card-visa-large.svg b/assets/images/companyCards/large/card-visa-large.svg
index 0f000c5652df..63289094956b 100644
--- a/assets/images/companyCards/large/card-visa-large.svg
+++ b/assets/images/companyCards/large/card-visa-large.svg
@@ -1,48 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/companyCards/large/card-wellsfargo-large.svg b/assets/images/companyCards/large/card-wellsfargo-large.svg
index ef9eb84a890d..588cff40115e 100644
--- a/assets/images/companyCards/large/card-wellsfargo-large.svg
+++ b/assets/images/companyCards/large/card-wellsfargo-large.svg
@@ -1,23 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/product-illustrations/emptystate__holdexpense.svg b/assets/images/product-illustrations/emptystate__holdexpense.svg
index d00738964047..2684926c768a 100644
--- a/assets/images/product-illustrations/emptystate__holdexpense.svg
+++ b/assets/images/product-illustrations/emptystate__holdexpense.svg
@@ -1,1207 +1 @@
-
-
-
+
\ No newline at end of file
diff --git a/assets/images/receipt-placeholder-plus.svg b/assets/images/receipt-placeholder-plus.svg
index 3ebc08b40b06..477f1f8585eb 100644
--- a/assets/images/receipt-placeholder-plus.svg
+++ b/assets/images/receipt-placeholder-plus.svg
@@ -1,17 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/simple-illustrations/emptystate__puzzlepieces.svg b/assets/images/simple-illustrations/emptystate__puzzlepieces.svg
index d137ce5dcff2..ad678e0ecd85 100644
--- a/assets/images/simple-illustrations/emptystate__puzzlepieces.svg
+++ b/assets/images/simple-illustrations/emptystate__puzzlepieces.svg
@@ -1,93 +1 @@
-
-
-
+
\ No newline at end of file
diff --git a/assets/images/simple-illustrations/simple-illustration__building.svg b/assets/images/simple-illustrations/simple-illustration__building.svg
index 94a7320d8471..4ba71bba9c82 100644
--- a/assets/images/simple-illustrations/simple-illustration__building.svg
+++ b/assets/images/simple-illustrations/simple-illustration__building.svg
@@ -1,47 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/simple-illustrations/simple-illustration__buildings.svg b/assets/images/simple-illustrations/simple-illustration__buildings.svg
index cb22c3a29ce4..d560a14b0d4d 100644
--- a/assets/images/simple-illustrations/simple-illustration__buildings.svg
+++ b/assets/images/simple-illustrations/simple-illustration__buildings.svg
@@ -1,55 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/simple-illustrations/simple-illustration__flash.svg b/assets/images/simple-illustrations/simple-illustration__flash.svg
index be8daf296aa1..4f30e2ecdb63 100644
--- a/assets/images/simple-illustrations/simple-illustration__flash.svg
+++ b/assets/images/simple-illustrations/simple-illustration__flash.svg
@@ -1,52 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/simple-illustrations/simple-illustration__mobileapp.svg b/assets/images/simple-illustrations/simple-illustration__mobileapp.svg
index 80682c942f81..5522bfc198a0 100644
--- a/assets/images/simple-illustrations/simple-illustration__mobileapp.svg
+++ b/assets/images/simple-illustrations/simple-illustration__mobileapp.svg
@@ -1,54 +1 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/assets/images/simple-illustrations/simple-illustration__perdiem.svg b/assets/images/simple-illustrations/simple-illustration__perdiem.svg
index ea5a865a2694..05d9ea4254be 100644
--- a/assets/images/simple-illustrations/simple-illustration__perdiem.svg
+++ b/assets/images/simple-illustrations/simple-illustration__perdiem.svg
@@ -1,82 +1 @@
-
-
-
+
\ No newline at end of file
diff --git a/assets/images/simple-illustrations/simple-illustration__pillow.svg b/assets/images/simple-illustrations/simple-illustration__pillow.svg
index 97a0811266ae..3ff85c19f1f0 100644
--- a/assets/images/simple-illustrations/simple-illustration__pillow.svg
+++ b/assets/images/simple-illustrations/simple-illustration__pillow.svg
@@ -1,28 +1 @@
-
+
\ No newline at end of file
diff --git a/assets/images/simple-illustrations/simple-illustration__realtimereports.svg b/assets/images/simple-illustrations/simple-illustration__realtimereports.svg
index 40fc3082a028..95a6009c5670 100644
--- a/assets/images/simple-illustrations/simple-illustration__realtimereports.svg
+++ b/assets/images/simple-illustrations/simple-illustration__realtimereports.svg
@@ -1,88 +1 @@
-
-
-
+
\ No newline at end of file
diff --git a/assets/images/simple-illustrations/simple-illustration__stopwatch.svg b/assets/images/simple-illustrations/simple-illustration__stopwatch.svg
index c348fd73337b..3c7f329daf52 100644
--- a/assets/images/simple-illustrations/simple-illustration__stopwatch.svg
+++ b/assets/images/simple-illustrations/simple-illustration__stopwatch.svg
@@ -1,41 +1 @@
-
-
-
+
\ No newline at end of file
diff --git a/assets/images/train.svg b/assets/images/train.svg
index 40d8c9d1af8a..ef04e95016ac 100644
--- a/assets/images/train.svg
+++ b/assets/images/train.svg
@@ -1,3 +1 @@
-
+
\ No newline at end of file
diff --git a/patches/react-native+0.76.3+030+disable-suggestion-pop-up.patch b/patches/react-native+0.76.3+030+disable-suggestion-pop-up.patch
new file mode 100644
index 000000000000..790c283edc8b
--- /dev/null
+++ b/patches/react-native+0.76.3+030+disable-suggestion-pop-up.patch
@@ -0,0 +1,325 @@
+diff --git a/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js b/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js
+index 0973ae8..d5e6cee 100644
+--- a/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js
++++ b/node_modules/react-native/Libraries/Components/TextInput/RCTTextInputViewConfig.js
+@@ -169,6 +169,7 @@ const RCTTextInputViewConfig = {
+ onChangeSync: true,
+ onKeyPressSync: true,
+ }),
++ disableKeyboardShortcuts: true,
+ },
+ };
+
+diff --git a/node_modules/react-native/Libraries/Components/TextInput/TextInput.d.ts b/node_modules/react-native/Libraries/Components/TextInput/TextInput.d.ts
+index 47cdcfc..22c42ae 100644
+--- a/node_modules/react-native/Libraries/Components/TextInput/TextInput.d.ts
++++ b/node_modules/react-native/Libraries/Components/TextInput/TextInput.d.ts
+@@ -136,6 +136,11 @@ export interface DocumentSelectionState extends EventEmitter {
+ * @see https://reactnative.dev/docs/textinput#props
+ */
+ export interface TextInputIOSProps {
++ /**
++ * If true, the keyboard shortcuts (undo/redo and copy buttons) are disabled. The default value is false.
++ */
++ disableKeyboardShortcuts?: boolean | undefined;
++
+ /**
+ * enum('never', 'while-editing', 'unless-editing', 'always')
+ * When the clear button should appear on the right side of the text view
+diff --git a/node_modules/react-native/Libraries/Components/TextInput/TextInput.flow.js b/node_modules/react-native/Libraries/Components/TextInput/TextInput.flow.js
+index 2f35731..a1455e7 100644
+--- a/node_modules/react-native/Libraries/Components/TextInput/TextInput.flow.js
++++ b/node_modules/react-native/Libraries/Components/TextInput/TextInput.flow.js
+@@ -224,6 +224,12 @@ export type enterKeyHintType =
+ type PasswordRules = string;
+
+ type IOSProps = $ReadOnly<{|
++ /**
++ * If true, the keyboard shortcuts (undo/redo and copy buttons) are disabled. The default value is false.
++ * @platform ios
++ */
++ disableKeyboardShortcuts?: ?boolean,
++
+ /**
+ * When the clear button should appear on the right side of the text view.
+ * This property is supported only for single-line TextInput component.
+diff --git a/node_modules/react-native/Libraries/Components/TextInput/TextInput.js b/node_modules/react-native/Libraries/Components/TextInput/TextInput.js
+index 2ffb38b..40e732f 100644
+--- a/node_modules/react-native/Libraries/Components/TextInput/TextInput.js
++++ b/node_modules/react-native/Libraries/Components/TextInput/TextInput.js
+@@ -267,6 +267,12 @@ export type enterKeyHintType =
+ type PasswordRules = string;
+
+ type IOSProps = $ReadOnly<{|
++ /**
++ * If true, the keyboard shortcuts (undo/redo and copy buttons) are disabled. The default value is false.
++ * @platform ios
++ */
++ disableKeyboardShortcuts?: ?boolean,
++
+ /**
+ * When the clear button should appear on the right side of the text view.
+ * This property is supported only for single-line TextInput component.
+diff --git a/node_modules/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.h b/node_modules/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.h
+index 205f994..3b528d2 100644
+--- a/node_modules/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.h
++++ b/node_modules/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.h
+@@ -38,6 +38,8 @@ NS_ASSUME_NONNULL_BEGIN
+
+ @property (nonatomic, strong, nullable) NSString *inputAccessoryViewID;
+
++@property (nonatomic, assign) BOOL disableKeyboardShortcuts;
++
+ @end
+
+ NS_ASSUME_NONNULL_END
+diff --git a/node_modules/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.mm b/node_modules/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.mm
+index 065a819..b521dd9 100644
+--- a/node_modules/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.mm
++++ b/node_modules/react-native/Libraries/Text/TextInput/Multiline/RCTUITextView.mm
+@@ -22,6 +22,8 @@ @implementation RCTUITextView {
+ UITextView *_detachedTextView;
+ RCTBackedTextViewDelegateAdapter *_textInputDelegateAdapter;
+ NSDictionary *_defaultTextAttributes;
++ NSArray *_initialValueLeadingBarButtonGroups;
++ NSArray *_initialValueTrailingBarButtonGroups;
+ }
+
+ static UIFont *defaultPlaceholderFont(void)
+@@ -56,6 +58,8 @@ - (instancetype)initWithFrame:(CGRect)frame
+ self.textContainer.lineFragmentPadding = 0;
+ self.scrollsToTop = NO;
+ self.scrollEnabled = YES;
++ _initialValueLeadingBarButtonGroups = nil;
++ _initialValueTrailingBarButtonGroups = nil;
+ }
+
+ return self;
+@@ -136,6 +140,25 @@ - (void)textDidChange
+ [self _invalidatePlaceholderVisibility];
+ }
+
++- (void)setDisableKeyboardShortcuts:(BOOL)disableKeyboardShortcuts
++{
++ // Initialize the initial values only once
++ if (_initialValueLeadingBarButtonGroups == nil) {
++ // Capture initial values of leading and trailing button groups
++ _initialValueLeadingBarButtonGroups = self.inputAssistantItem.leadingBarButtonGroups;
++ _initialValueTrailingBarButtonGroups = self.inputAssistantItem.trailingBarButtonGroups;
++ }
++
++ if (disableKeyboardShortcuts) {
++ self.inputAssistantItem.leadingBarButtonGroups = @[];
++ self.inputAssistantItem.trailingBarButtonGroups = @[];
++ } else {
++ // Restore the initial values
++ self.inputAssistantItem.leadingBarButtonGroups = _initialValueLeadingBarButtonGroups;
++ self.inputAssistantItem.trailingBarButtonGroups = _initialValueTrailingBarButtonGroups;
++ }
++}
++
+ #pragma mark - Overrides
+
+ - (void)setFont:(UIFont *)font
+diff --git a/node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h b/node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h
+index cc51013..26a112f 100644
+--- a/node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h
++++ b/node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h
+@@ -51,6 +51,8 @@ NS_ASSUME_NONNULL_BEGIN
+ // Use `attributedText.string` instead.
+ @property (nonatomic, copy, nullable) NSString *text NS_UNAVAILABLE;
+
++@property (nonatomic, assign) BOOL disableKeyboardShortcuts;
++
+ @end
+
+ NS_ASSUME_NONNULL_END
+diff --git a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm
+index 6047486..5d1e97b 100644
+--- a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm
++++ b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.mm
+@@ -30,6 +30,8 @@ @implementation RCTBaseTextInputView {
+ BOOL _hasInputAccessoryView;
+ NSString *_Nullable _predictedText;
+ BOOL _didMoveToWindow;
++ NSArray *_initialValueLeadingBarButtonGroups;
++ NSArray *_initialValueTrailingBarButtonGroups;
+ }
+
+ - (void)reactUpdateResponderOffsetForScrollView:(RCTScrollView *)scrollView
+@@ -64,6 +66,8 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge
+ _bridge = bridge;
+ _eventDispatcher = bridge.eventDispatcher;
+ [self initializeReturnKeyType];
++ _initialValueLeadingBarButtonGroups = nil;
++ _initialValueTrailingBarButtonGroups = nil;
+ }
+
+ return self;
+@@ -374,6 +378,25 @@ - (void)setShowSoftInputOnFocus:(BOOL)showSoftInputOnFocus
+ }
+ }
+
++- (void)setDisableKeyboardShortcuts:(BOOL)disableKeyboardShortcuts
++{
++ // Initialize the initial values only once
++ if (_initialValueLeadingBarButtonGroups == nil) {
++ // Capture initial values of leading and trailing button groups
++ _initialValueLeadingBarButtonGroups = self.backedTextInputView.inputAssistantItem.leadingBarButtonGroups;
++ _initialValueTrailingBarButtonGroups = self.backedTextInputView.inputAssistantItem.trailingBarButtonGroups;
++ }
++
++ if (disableKeyboardShortcuts) {
++ self.backedTextInputView.inputAssistantItem.leadingBarButtonGroups = @[];
++ self.backedTextInputView.inputAssistantItem.trailingBarButtonGroups = @[];
++ } else {
++ // Restore the initial values
++ self.backedTextInputView.inputAssistantItem.leadingBarButtonGroups = _initialValueLeadingBarButtonGroups;
++ self.backedTextInputView.inputAssistantItem.trailingBarButtonGroups = _initialValueTrailingBarButtonGroups;
++ }
++}
++
+ #pragma mark - RCTBackedTextInputDelegate
+
+ - (BOOL)textInputShouldBeginEditing
+diff --git a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputViewManager.mm b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputViewManager.mm
+index e367394..08ec761 100644
+--- a/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputViewManager.mm
++++ b/node_modules/react-native/Libraries/Text/TextInput/RCTBaseTextInputViewManager.mm
+@@ -70,6 +70,8 @@ @implementation RCTBaseTextInputViewManager {
+
+ RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger)
+
++RCT_EXPORT_VIEW_PROPERTY(disableKeyboardShortcuts, BOOL)
++
+ RCT_EXPORT_SHADOW_PROPERTY(text, NSString)
+ RCT_EXPORT_SHADOW_PROPERTY(placeholder, NSString)
+ RCT_EXPORT_SHADOW_PROPERTY(onContentSizeChange, RCTDirectEventBlock)
+diff --git a/node_modules/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.h b/node_modules/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.h
+index 91f8eb0..fbf9f32 100644
+--- a/node_modules/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.h
++++ b/node_modules/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.h
+@@ -33,6 +33,7 @@ NS_ASSUME_NONNULL_BEGIN
+ @property (nonatomic, assign, readonly) CGFloat zoomScale;
+ @property (nonatomic, assign, readonly) CGPoint contentOffset;
+ @property (nonatomic, assign, readonly) UIEdgeInsets contentInset;
++@property (nonatomic, assign) BOOL disableKeyboardShortcuts;
+
+ @end
+
+diff --git a/node_modules/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.mm b/node_modules/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.mm
+index 667e646..617f05f 100644
+--- a/node_modules/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.mm
++++ b/node_modules/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.mm
+@@ -19,6 +19,8 @@
+ @implementation RCTUITextField {
+ RCTBackedTextFieldDelegateAdapter *_textInputDelegateAdapter;
+ NSDictionary *_defaultTextAttributes;
++ NSArray *_initialValueLeadingBarButtonGroups;
++ NSArray *_initialValueTrailingBarButtonGroups;
+ }
+
+ - (instancetype)initWithFrame:(CGRect)frame
+@@ -31,6 +33,8 @@ - (instancetype)initWithFrame:(CGRect)frame
+
+ _textInputDelegateAdapter = [[RCTBackedTextFieldDelegateAdapter alloc] initWithTextField:self];
+ _scrollEnabled = YES;
++ _initialValueLeadingBarButtonGroups = nil;
++ _initialValueTrailingBarButtonGroups = nil;
+ }
+
+ return self;
+@@ -119,6 +123,25 @@ - (void)setSecureTextEntry:(BOOL)secureTextEntry
+ self.attributedText = originalText;
+ }
+
++- (void)setDisableKeyboardShortcuts:(BOOL)disableKeyboardShortcuts
++{
++ // Initialize the initial values only once
++ if (_initialValueLeadingBarButtonGroups == nil) {
++ // Capture initial values of leading and trailing button groups
++ _initialValueLeadingBarButtonGroups = self.inputAssistantItem.leadingBarButtonGroups;
++ _initialValueTrailingBarButtonGroups = self.inputAssistantItem.trailingBarButtonGroups;
++ }
++
++ if (disableKeyboardShortcuts) {
++ self.inputAssistantItem.leadingBarButtonGroups = @[];
++ self.inputAssistantItem.trailingBarButtonGroups = @[];
++ } else {
++ // Restore the initial values
++ self.inputAssistantItem.leadingBarButtonGroups = _initialValueLeadingBarButtonGroups;
++ self.inputAssistantItem.trailingBarButtonGroups = _initialValueTrailingBarButtonGroups;
++ }
++}
++
+ #pragma mark - Placeholder
+
+ - (NSDictionary *)_placeholderTextAttributes
+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 2e7b1a1..abd91ef 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
+@@ -278,6 +278,11 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &
+ if (newTextInputProps.inputAccessoryViewID != oldTextInputProps.inputAccessoryViewID) {
+ _backedTextInputView.inputAccessoryViewID = RCTNSStringFromString(newTextInputProps.inputAccessoryViewID);
+ }
++
++ if (newTextInputProps.disableKeyboardShortcuts != oldTextInputProps.disableKeyboardShortcuts) {
++ _backedTextInputView.disableKeyboardShortcuts = newTextInputProps.disableKeyboardShortcuts;
++ }
++
+ [super updateProps:props oldProps:oldProps];
+
+ [self setDefaultInputAccessoryView];
+diff --git a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm
+index 6345758..92c56b4 100644
+--- a/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm
++++ b/node_modules/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm
+@@ -45,6 +45,7 @@ void RCTCopyBackedTextInput(
+ toTextInput.textContentType = fromTextInput.textContentType;
+ toTextInput.smartInsertDeleteType = fromTextInput.smartInsertDeleteType;
+ toTextInput.passwordRules = fromTextInput.passwordRules;
++ toTextInput.disableKeyboardShortcuts = fromTextInput.disableKeyboardShortcuts;
+
+ [toTextInput setSelectedTextRange:fromTextInput.selectedTextRange notifyDelegate:NO];
+ }
+diff --git a/node_modules/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.cpp b/node_modules/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.cpp
+index ec0f350..56c3b4f 100644
+--- a/node_modules/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.cpp
++++ b/node_modules/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.cpp
+@@ -102,7 +102,13 @@ BaseTextInputProps::BaseTextInputProps(
+ rawProps,
+ "autoCapitalize",
+ sourceProps.autoCapitalize,
+- {})) {}
++ {})),
++ disableKeyboardShortcuts(convertRawProp(
++ context,
++ rawProps,
++ "disableKeyboardShortcuts",
++ sourceProps.disableKeyboardShortcuts,
++ {false})) {}
+
+ void BaseTextInputProps::setProp(
+ const PropsParserContext& context,
+@@ -180,6 +186,7 @@ void BaseTextInputProps::setProp(
+ RAW_SET_PROP_SWITCH_CASE_BASIC(text);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(mostRecentEventCount);
+ RAW_SET_PROP_SWITCH_CASE_BASIC(autoCapitalize);
++ RAW_SET_PROP_SWITCH_CASE_BASIC(disableKeyboardShortcuts);
+ }
+ }
+
+diff --git a/node_modules/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.h b/node_modules/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.h
+index bff69fe..27782a1 100644
+--- a/node_modules/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.h
++++ b/node_modules/react-native/ReactCommon/react/renderer/components/textinput/BaseTextInputProps.h
+@@ -63,6 +63,8 @@ class BaseTextInputProps : public ViewProps, public BaseTextProps {
+ bool autoFocus{false};
+
+ std::string autoCapitalize{};
++
++ bool disableKeyboardShortcuts{false};
+ };
+
+ } // namespace facebook::react
diff --git a/src/CONST.ts b/src/CONST.ts
index 12e92c26479f..e4f782be13d6 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -177,9 +177,9 @@ const onboardingPersonalSpendMessage: OnboardingMessage = {
'Here’s how to track an expense:\n' +
'\n' +
'1. Click the green *+* button.\n' +
- '2. Choose *Track expense*.\n' +
+ '2. Choose *Create expense*.\n' +
'3. Enter an amount or scan a receipt.\n' +
- '4. Click *Track*.\n' +
+ '4. Click *Create*.\n' +
'\n' +
'And you’re done! Yep, it’s that easy.',
},
diff --git a/src/components/AmountTextInput.tsx b/src/components/AmountTextInput.tsx
index 12189d22dba0..256d95ba8419 100644
--- a/src/components/AmountTextInput.tsx
+++ b/src/components/AmountTextInput.tsx
@@ -84,6 +84,7 @@ function AmountTextInput(
// Setting both autoCorrect and spellCheck to false will hide the suggestion.
autoCorrect={false}
spellCheck={false}
+ disableKeyboardShortcuts
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
/>
diff --git a/src/components/BrokenConnectionDescription.tsx b/src/components/BrokenConnectionDescription.tsx
index a1b9bd62ffa2..078cbed25631 100644
--- a/src/components/BrokenConnectionDescription.tsx
+++ b/src/components/BrokenConnectionDescription.tsx
@@ -3,8 +3,8 @@ import type {OnyxEntry} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
-import * as PolicyUtils from '@libs/PolicyUtils';
-import * as ReportUtils from '@libs/ReportUtils';
+import {isInstantSubmitEnabled, isPolicyAdmin as isPolicyAdminPolicyUtils} from '@libs/PolicyUtils';
+import {isCurrentUserSubmitter, isProcessingReport, isReportApproved, isReportManuallyReimbursed} from '@libs/ReportUtils';
import Navigation from '@navigation/Navigation';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -26,11 +26,11 @@ type BrokenConnectionDescriptionProps = {
function BrokenConnectionDescription({transactionID, policy, report}: BrokenConnectionDescriptionProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
- const [transactionViolations] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`);
+ const [transactionViolations] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID ?? CONST.DEFAULT_NUMBER_ID}`);
const brokenConnection530Error = transactionViolations?.find((violation) => violation.data?.rterType === CONST.RTER_VIOLATION_TYPES.BROKEN_CARD_CONNECTION_530);
const brokenConnectionError = transactionViolations?.find((violation) => violation.data?.rterType === CONST.RTER_VIOLATION_TYPES.BROKEN_CARD_CONNECTION);
- const isPolicyAdmin = PolicyUtils.isPolicyAdmin(policy);
+ const isPolicyAdmin = isPolicyAdminPolicyUtils(policy);
if (!brokenConnection530Error && !brokenConnectionError) {
return '';
@@ -40,7 +40,7 @@ function BrokenConnectionDescription({transactionID, policy, report}: BrokenConn
return translate('violations.brokenConnection530Error');
}
- if (isPolicyAdmin && !ReportUtils.isCurrentUserSubmitter(report?.reportID)) {
+ if (isPolicyAdmin && !isCurrentUserSubmitter(report?.reportID)) {
return (
<>
{`${translate('violations.adminBrokenConnectionError')}`}
@@ -53,7 +53,7 @@ function BrokenConnectionDescription({transactionID, policy, report}: BrokenConn
);
}
- if (ReportUtils.isReportApproved(report) || ReportUtils.isReportManuallyReimbursed(report) || (ReportUtils.isProcessingReport(report) && !PolicyUtils.isInstantSubmitEnabled(policy))) {
+ if (isReportApproved(report) || isReportManuallyReimbursed(report) || (isProcessingReport(report) && !isInstantSubmitEnabled(policy))) {
return translate('violations.memberBrokenConnectionError');
}
diff --git a/src/components/MoneyReportHeader.tsx b/src/components/MoneyReportHeader.tsx
index 1654314e576d..38d859def1f7 100644
--- a/src/components/MoneyReportHeader.tsx
+++ b/src/components/MoneyReportHeader.tsx
@@ -8,7 +8,6 @@ import useNetwork from '@hooks/useNetwork';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
-import {getCurrentUserAccountID} from '@libs/actions/Report';
import {convertToDisplayString} from '@libs/CurrencyUtils';
import Navigation from '@libs/Navigation/Navigation';
import {getConnectedIntegration, isPolicyAdmin} from '@libs/PolicyUtils';
@@ -30,7 +29,6 @@ import {
isClosedExpenseReportWithNoExpenses,
isCurrentUserSubmitter,
isInvoiceReport,
- isOpenExpenseReport,
navigateBackOnDeleteTransaction,
} from '@libs/ReportUtils';
import {
@@ -45,7 +43,17 @@ import {
shouldShowBrokenConnectionViolation as shouldShowBrokenConnectionViolationTransactionUtils,
} from '@libs/TransactionUtils';
import variables from '@styles/variables';
-import {approveMoneyRequest, canApproveIOU, canIOUBePaid as canIOUBePaidAction, deleteMoneyRequest, deleteTrackExpense, payInvoice, payMoneyRequest, submitReport} from '@userActions/IOU';
+import {
+ approveMoneyRequest,
+ canApproveIOU,
+ canIOUBePaid as canIOUBePaidAction,
+ canSubmitReport,
+ deleteMoneyRequest,
+ deleteTrackExpense,
+ payInvoice,
+ payMoneyRequest,
+ submitReport,
+} from '@userActions/IOU';
import {markAsCash as markAsCashAction} from '@userActions/Transaction';
import CONST from '@src/CONST';
import useDelegateUserDetails from '@src/hooks/useDelegateUserDetails';
@@ -134,15 +142,14 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
const allTransactions = useMemo(() => getAllReportTransactions(moneyRequestReport?.reportID, transactions), [moneyRequestReport?.reportID, transactions]);
const canAllowSettlement = hasUpdatedTotal(moneyRequestReport, policy);
const policyType = policy?.type;
- const isDraft = isOpenExpenseReport(moneyRequestReport);
const connectedIntegration = getConnectedIntegration(policy);
const navigateBackToAfterDelete = useRef();
const hasHeldExpenses = hasHeldExpensesReportUtils(moneyRequestReport?.reportID);
const hasScanningReceipt = getTransactionsWithReceipts(moneyRequestReport?.reportID).some((t) => isReceiptBeingScanned(t));
const hasOnlyPendingTransactions = allTransactions.length > 0 && allTransactions.every((t) => isExpensifyCardTransaction(t) && isPending(t));
- const transactionIDs = allTransactions.map((t) => t.transactionID);
- const hasAllPendingRTERViolations = allHavePendingRTERViolation([transaction?.transactionID]);
- const shouldShowBrokenConnectionViolation = shouldShowBrokenConnectionViolationTransactionUtils(transaction?.transactionID, moneyRequestReport, policy);
+ const transactionIDs = allTransactions.map((t) => t.transactionID) ?? [];
+ const hasAllPendingRTERViolations = allHavePendingRTERViolation(transactionIDs);
+ const shouldShowBrokenConnectionViolation = shouldShowBrokenConnectionViolationTransactionUtils(transactionIDs, moneyRequestReport, policy);
const hasOnlyHeldExpenses = hasOnlyHeldExpensesReportUtils(moneyRequestReport?.reportID);
const isPayAtEndExpense = isPayAtEndExpenseTransactionUtils(transaction);
const isArchivedReport = isArchivedReportUtils(moneyRequestReport);
@@ -165,22 +172,18 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
const shouldDisableApproveButton = shouldShowApproveButton && !isAllowedToApproveExpenseReport(moneyRequestReport);
- const currentUserAccountID = getCurrentUserAccountID();
const isAdmin = policy?.role === CONST.POLICY.ROLE.ADMIN;
- const shouldShowSubmitButton =
- !!moneyRequestReport &&
- !isArchivedReport &&
- isDraft &&
- reimbursableSpend !== 0 &&
- !hasAllPendingRTERViolations &&
- !shouldShowBrokenConnectionViolation &&
- (moneyRequestReport?.ownerAccountID === currentUserAccountID || isAdmin || moneyRequestReport?.managerID === currentUserAccountID);
+ const shouldShowSubmitButton = canSubmitReport(moneyRequestReport, policy, transactionIDs);
const shouldShowExportIntegrationButton = !shouldShowPayButton && !shouldShowSubmitButton && connectedIntegration && isAdmin && canBeExported(moneyRequestReport);
const shouldShowSettlementButton =
- (shouldShowPayButton || shouldShowApproveButton) && !hasAllPendingRTERViolations && !shouldShowExportIntegrationButton && !shouldShowBrokenConnectionViolation;
+ !shouldShowSubmitButton &&
+ (shouldShowPayButton || shouldShowApproveButton) &&
+ !hasAllPendingRTERViolations &&
+ !shouldShowExportIntegrationButton &&
+ !shouldShowBrokenConnectionViolation;
const shouldDisableSubmitButton = shouldShowSubmitButton && !isAllowedToSubmitDraftExpenseReport(moneyRequestReport);
const isFromPaidPolicy = policyType === CONST.POLICY.TYPE.TEAM || policyType === CONST.POLICY.TYPE.CORPORATE;
@@ -399,7 +402,7 @@ function MoneyReportHeader({policy, report: moneyRequestReport, transactionThrea
/>
)}
- {shouldShowSubmitButton && !shouldUseNarrowLayout && (
+ {!!moneyRequestReport && shouldShowSubmitButton && !shouldUseNarrowLayout && (
)}
- {shouldShowSubmitButton && shouldUseNarrowLayout && (
+ {!!moneyRequestReport && shouldShowSubmitButton && shouldUseNarrowLayout && (