Skip to content

Commit

Permalink
Merge pull request #42630 from margelo/perunt/inline-auto-suggestion
Browse files Browse the repository at this point in the history
Inline auto suggestion
  • Loading branch information
puneetlath authored Jun 18, 2024
2 parents 8dea185 + 305eebb commit 2995925
Show file tree
Hide file tree
Showing 38 changed files with 610 additions and 189 deletions.
23 changes: 23 additions & 0 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1303,6 +1303,25 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-keyboard-controller (1.12.2):
- glog
- hermes-engine
- RCT-Folly (= 2022.05.16.00)
- RCTRequired
- RCTTypeSafety
- React-Codegen
- React-Core
- React-debug
- React-Fabric
- React-graphics
- React-ImageManager
- React-NativeModulesApple
- React-RCTFabric
- React-rendererdebug
- React-utils
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-launch-arguments (4.0.2):
- React
- react-native-netinfo (11.2.1):
Expand Down Expand Up @@ -2137,6 +2156,7 @@ DEPENDENCIES:
- "react-native-geolocation (from `../node_modules/@react-native-community/geolocation`)"
- react-native-image-picker (from `../node_modules/react-native-image-picker`)
- react-native-key-command (from `../node_modules/react-native-key-command`)
- react-native-keyboard-controller (from `../node_modules/react-native-keyboard-controller`)
- react-native-launch-arguments (from `../node_modules/react-native-launch-arguments`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- react-native-pager-view (from `../node_modules/react-native-pager-view`)
Expand Down Expand Up @@ -2335,6 +2355,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-image-picker"
react-native-key-command:
:path: "../node_modules/react-native-key-command"
react-native-keyboard-controller:
:path: "../node_modules/react-native-keyboard-controller"
react-native-launch-arguments:
:path: "../node_modules/react-native-launch-arguments"
react-native-netinfo:
Expand Down Expand Up @@ -2541,6 +2563,7 @@ SPEC CHECKSUMS:
react-native-geolocation: f9e92eb774cb30ac1e099f34b3a94f03b4db7eb3
react-native-image-picker: f8a13ff106bcc7eb00c71ce11fdc36aac2a44440
react-native-key-command: 28ccfa09520e7d7e30739480dea4df003493bfe8
react-native-keyboard-controller: 47c01b0741ae5fc84e53cf282e61cfa5c2edb19b
react-native-launch-arguments: 5f41e0abf88a15e3c5309b8875d6fd5ac43df49d
react-native-netinfo: 02d31de0e08ab043d48f2a1a8baade109d7b6ca5
react-native-pager-view: ccd4bbf9fc7effaf8f91f8dae43389844d9ef9fa
Expand Down
3 changes: 3 additions & 0 deletions jest/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,6 @@ jest.mock('react-native-sound', () => {
jest.mock('react-native-share', () => ({
default: jest.fn(),
}));

// eslint-disable-next-line @typescript-eslint/no-unsafe-return
jest.mock('react-native-keyboard-controller', () => require('react-native-keyboard-controller/jest'));
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
"react-native-image-picker": "^7.0.3",
"react-native-image-size": "git+https://github.com/Expensify/react-native-image-size#bf3ad41a61c4f6f80ed4d497599ef5247a2dd002",
"react-native-key-command": "^1.0.8",
"react-native-keyboard-controller": "^1.12.2",
"react-native-launch-arguments": "^4.0.2",
"react-native-linear-gradient": "^2.8.1",
"react-native-localize": "^2.2.6",
Expand Down
70 changes: 70 additions & 0 deletions patches/react-native+0.73.4+016+iOS-textinput-onscroll-event.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
diff --git a/node_modules/react-native/ReactCommon/react/renderer/components/textinput/iostextinput/react/renderer/components/iostextinput/TextInputEventEmitter.cpp b/node_modules/react-native/ReactCommon/react/renderer/components/textinput/iostextinput/react/renderer/components/iostextinput/TextInputEventEmitter.cpp
index 88ae3f3..497569a 100644
--- a/node_modules/react-native/ReactCommon/react/renderer/components/textinput/iostextinput/react/renderer/components/iostextinput/TextInputEventEmitter.cpp
+++ b/node_modules/react-native/ReactCommon/react/renderer/components/textinput/iostextinput/react/renderer/components/iostextinput/TextInputEventEmitter.cpp
@@ -36,6 +36,54 @@ static jsi::Value textInputMetricsPayload(
return payload;
};

+static jsi::Value textInputMetricsScrollPayload(
+ jsi::Runtime& runtime,
+ const TextInputMetrics& textInputMetrics) {
+ auto payload = jsi::Object(runtime);
+
+ {
+ auto contentOffset = jsi::Object(runtime);
+ contentOffset.setProperty(runtime, "x", textInputMetrics.contentOffset.x);
+ contentOffset.setProperty(runtime, "y", textInputMetrics.contentOffset.y);
+ payload.setProperty(runtime, "contentOffset", contentOffset);
+ }
+
+ {
+ auto contentInset = jsi::Object(runtime);
+ contentInset.setProperty(runtime, "top", textInputMetrics.contentInset.top);
+ contentInset.setProperty(
+ runtime, "left", textInputMetrics.contentInset.left);
+ contentInset.setProperty(
+ runtime, "bottom", textInputMetrics.contentInset.bottom);
+ contentInset.setProperty(
+ runtime, "right", textInputMetrics.contentInset.right);
+ payload.setProperty(runtime, "contentInset", contentInset);
+ }
+
+ {
+ auto contentSize = jsi::Object(runtime);
+ contentSize.setProperty(
+ runtime, "width", textInputMetrics.contentSize.width);
+ contentSize.setProperty(
+ runtime, "height", textInputMetrics.contentSize.height);
+ payload.setProperty(runtime, "contentSize", contentSize);
+ }
+
+ {
+ auto layoutMeasurement = jsi::Object(runtime);
+ layoutMeasurement.setProperty(
+ runtime, "width", textInputMetrics.layoutMeasurement.width);
+ layoutMeasurement.setProperty(
+ runtime, "height", textInputMetrics.layoutMeasurement.height);
+ payload.setProperty(runtime, "layoutMeasurement", layoutMeasurement);
+ }
+
+ payload.setProperty(runtime, "zoomScale", textInputMetrics.zoomScale ?: 1);
+
+
+ return payload;
+ };
+
static jsi::Value textInputMetricsContentSizePayload(
jsi::Runtime& runtime,
const TextInputMetrics& textInputMetrics) {
@@ -140,7 +188,9 @@ void TextInputEventEmitter::onKeyPressSync(

void TextInputEventEmitter::onScroll(
const TextInputMetrics& textInputMetrics) const {
- dispatchTextInputEvent("scroll", textInputMetrics);
+ dispatchEvent("scroll", [textInputMetrics](jsi::Runtime& runtime) {
+ return textInputMetricsScrollPayload(runtime, textInputMetrics);
+ });
}

void TextInputEventEmitter::dispatchTextInputEvent(
39 changes: 39 additions & 0 deletions patches/react-native-keyboard-controller+1.12.2.patch.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
diff --git a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt
index 83884d8..5d9e989 100644
--- a/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt
+++ b/node_modules/react-native-keyboard-controller/android/src/main/java/com/reactnativekeyboardcontroller/views/EdgeToEdgeReactViewGroup.kt
@@ -99,12 +99,12 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
}

private fun goToEdgeToEdge(edgeToEdge: Boolean) {
- reactContext.currentActivity?.let {
- WindowCompat.setDecorFitsSystemWindows(
- it.window,
- !edgeToEdge,
- )
- }
+ // reactContext.currentActivity?.let {
+ // WindowCompat.setDecorFitsSystemWindows(
+ // it.window,
+ // !edgeToEdge,
+ // )
+ // }
}

private fun setupKeyboardCallbacks() {
@@ -158,13 +158,13 @@ class EdgeToEdgeReactViewGroup(private val reactContext: ThemedReactContext) : R
// region State managers
private fun enable() {
this.goToEdgeToEdge(true)
- this.setupWindowInsets()
+ // this.setupWindowInsets()
this.setupKeyboardCallbacks()
}

private fun disable() {
this.goToEdgeToEdge(false)
- this.setupWindowInsets()
+ // this.setupWindowInsets()
this.removeKeyboardCallbacks()
}
// endregion
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {PortalProvider} from '@gorhom/portal';
import React from 'react';
import {LogBox} from 'react-native';
import {GestureHandlerRootView} from 'react-native-gesture-handler';
import {KeyboardProvider} from 'react-native-keyboard-controller';
import {PickerStateProvider} from 'react-native-picker-select';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import '../wdyr';
Expand Down Expand Up @@ -84,6 +85,7 @@ function App({url}: AppProps) {
FullScreenContextProvider,
VolumeContextProvider,
VideoPopoverMenuContextProvider,
KeyboardProvider,
]}
>
<CustomStatusBarAndBackground />
Expand Down
2 changes: 2 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,8 @@ const CONST = {
MAX_AMOUNT_OF_SUGGESTIONS: 20,
MAX_AMOUNT_OF_VISIBLE_SUGGESTIONS_IN_CONTAINER: 5,
HERE_TEXT: '@here',
SUGGESTION_BOX_MAX_SAFE_DISTANCE: 38,
BIG_SCREEN_SUGGESTION_WIDTH: 300,
},
COMPOSER_MAX_HEIGHT: 125,
CHAT_FOOTER_SECONDARY_ROW_HEIGHT: 15,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function getBottomSuggestionPadding(): number {
return 16;
}

export default getBottomSuggestionPadding;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function getBottomSuggestionPadding(): number {
return 0;
}

export default getBottomSuggestionPadding;
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {Portal} from '@gorhom/portal';
import React, {useMemo} from 'react';
import {View} from 'react-native';
import BaseAutoCompleteSuggestions from '@components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions';
import useStyleUtils from '@hooks/useStyleUtils';
import getBottomSuggestionPadding from './getBottomSuggestionPadding';
import type {AutoCompleteSuggestionsPortalProps} from './types';

function AutoCompleteSuggestionsPortal<TSuggestion>({left = 0, width = 0, bottom = 0, ...props}: AutoCompleteSuggestionsPortalProps<TSuggestion>) {
const StyleUtils = useStyleUtils();
const styles = useMemo(() => StyleUtils.getBaseAutoCompleteSuggestionContainerStyle({left, width, bottom: bottom + getBottomSuggestionPadding()}), [StyleUtils, left, width, bottom]);

if (!width) {
return null;
}

return (
<Portal hostName="suggestions">
<View style={styles}>
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
<BaseAutoCompleteSuggestions<TSuggestion>
width={width}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
/>
</View>
</Portal>
);
}

AutoCompleteSuggestionsPortal.displayName = 'AutoCompleteSuggestionsPortal';

export default AutoCompleteSuggestionsPortal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import type {ReactElement} from 'react';
import ReactDOM from 'react-dom';
import {View} from 'react-native';
import BaseAutoCompleteSuggestions from '@components/AutoCompleteSuggestions/BaseAutoCompleteSuggestions';
import useStyleUtils from '@hooks/useStyleUtils';
import getBottomSuggestionPadding from './getBottomSuggestionPadding';
import type {AutoCompleteSuggestionsPortalProps} from './types';

/**
* On the mobile-web platform, when long-pressing on auto-complete suggestions,
* we need to prevent focus shifting to avoid blurring the main input (which makes the suggestions picker close and fires the onSelect callback).
* The desired pattern for all platforms is to do nothing on long-press.
* On the native platform, tapping on auto-complete suggestions will not blur the main input.
*/

function AutoCompleteSuggestionsPortal<TSuggestion>({left = 0, width = 0, bottom = 0, ...props}: AutoCompleteSuggestionsPortalProps<TSuggestion>): ReactElement | null | false {
const StyleUtils = useStyleUtils();

const bodyElement = document.querySelector('body');

const componentToRender = (
<BaseAutoCompleteSuggestions<TSuggestion>
width={width}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
/>
);

return (
!!width &&
bodyElement &&
ReactDOM.createPortal(
<View style={StyleUtils.getBaseAutoCompleteSuggestionContainerStyle({left, width, bottom: bottom - getBottomSuggestionPadding()})}>{componentToRender}</View>,
bodyElement,
)
);
}

AutoCompleteSuggestionsPortal.displayName = 'AutoCompleteSuggestionsPortal';

export default AutoCompleteSuggestionsPortal;
export type {AutoCompleteSuggestionsPortalProps};
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type {AutoCompleteSuggestionsProps} from '@components/AutoCompleteSuggestions/types';

type ExternalProps<TSuggestion> = Omit<AutoCompleteSuggestionsProps<TSuggestion>, 'measureParentContainerAndReportCursor'>;

type AutoCompleteSuggestionsPortalProps<TSuggestion> = ExternalProps<TSuggestion> & {
left: number;
width: number;
bottom: number;
measuredHeightOfSuggestionRows: number;
};

// eslint-disable-next-line import/prefer-default-export
export type {AutoCompleteSuggestionsPortalProps};
Loading

0 comments on commit 2995925

Please sign in to comment.