diff --git a/app/components/search/index.tsx b/app/components/search/index.tsx index 756ff377d7..0d5c048253 100644 --- a/app/components/search/index.tsx +++ b/app/components/search/index.tsx @@ -47,6 +47,7 @@ export type SearchRef = { cancel: () => void; clear: () => void; focus: () => void; + setNativeProps(nativeProps: object): void; } const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ @@ -151,6 +152,9 @@ const Search = forwardRef((props: SearchProps, ref) => { focus: () => { searchRef.current?.focus(); }, + setNativeProps: (nativeProps: object) => { + searchRef.current?.setNativeProps(nativeProps); + }, }), [searchRef]); return ( diff --git a/app/screens/home/search/initial/initial.tsx b/app/screens/home/search/initial/initial.tsx index 65c94bf625..76b0a3611f 100644 --- a/app/screens/home/search/initial/initial.tsx +++ b/app/screens/home/search/initial/initial.tsx @@ -1,9 +1,10 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React from 'react'; +import React, {Dispatch, RefObject, SetStateAction} from 'react'; import Animated from 'react-native-reanimated'; +import {SearchRef} from '@components/search'; import {TeamModel} from '@database/models/server'; import Modifiers from './modifiers'; @@ -15,19 +16,21 @@ type Props = { recentSearches: TeamSearchHistoryModel[]; scrollEnabled: Animated.SharedValue; searchValue?: string; - setRecentValue: (value: string) => void; - setSearchValue: (value: string) => void; + setRecentValue: Dispatch>; + searchRef: RefObject; + setSearchValue: Dispatch>; setTeamId: (value: string) => void; teamId: string; teamName: string; teams: TeamModel[]; } -const Initial = ({recentSearches, scrollEnabled, searchValue, setRecentValue, teamId, teamName, teams, setTeamId, setSearchValue}: Props) => { +const Initial = ({recentSearches, scrollEnabled, searchValue, setRecentValue, searchRef, teamId, teamName, teams, setTeamId, setSearchValue}: Props) => { return ( <> { term: '""', testID: 'search.modifier.phrases', description: formatMessage({id: 'mobile.search.modifier.phrases', defaultMessage: ' messages with phrases'}), + cursorPosition: -1, }, ]; return sectionsData; @@ -80,13 +82,14 @@ const getModifiersSectionsData = (intl: IntlShape): ModifierItem[] => { type Props = { scrollEnabled: Animated.SharedValue; - setSearchValue: (value: string) => void; + searchRef: RefObject; + setSearchValue: Dispatch>; searchValue?: string; setTeamId: (id: string) => void; teamId: string; teams: TeamModel[]; } -const Modifiers = ({scrollEnabled, searchValue, setSearchValue, setTeamId, teamId, teams}: Props) => { +const Modifiers = ({scrollEnabled, searchValue, setSearchValue, searchRef, setTeamId, teamId, teams}: Props) => { const theme = useTheme(); const intl = useIntl(); @@ -130,6 +133,7 @@ const Modifiers = ({scrollEnabled, searchValue, setSearchValue, setTeamId, teamI diff --git a/app/screens/home/search/initial/modifiers/modifier.tsx b/app/screens/home/search/initial/modifiers/modifier.tsx index a3d0d0e4af..e2f303bcf6 100644 --- a/app/screens/home/search/initial/modifiers/modifier.tsx +++ b/app/screens/home/search/initial/modifiers/modifier.tsx @@ -1,10 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useCallback} from 'react'; +import React, {Dispatch, RefObject, SetStateAction, useCallback} from 'react'; import {StyleSheet} from 'react-native'; import OptionItem from '@components/option_item'; +import {SearchRef} from '@components/search'; import {preventDoubleTap} from '@utils/tap'; const styles = StyleSheet.create({ @@ -14,18 +15,20 @@ const styles = StyleSheet.create({ }); export type ModifierItem = { - description: string; - testID: string; - term: string; + cursorPosition?: number; + description: string; + testID: string; + term: string; } type Props = { item: ModifierItem; - setSearchValue: (value: string) => void; + setSearchValue: Dispatch>; searchValue?: string; + searchRef: RefObject; } -const Modifier = ({item, searchValue, setSearchValue}: Props) => { +const Modifier = ({item, searchRef, searchValue, setSearchValue}: Props) => { const handlePress = useCallback(() => { addModifierTerm(item.term); }, [item.term, searchValue]); @@ -41,6 +44,12 @@ const Modifier = ({item, searchValue, setSearchValue}: Props) => { } setSearchValue(newValue); + if (item.cursorPosition) { + const position = newValue.length + item.cursorPosition; + requestAnimationFrame(() => { + searchRef.current?.setNativeProps({selection: {start: position, end: position}}); + }); + } }); return ( diff --git a/app/screens/home/search/search.tsx b/app/screens/home/search/search.tsx index 45c7d145d8..8a9c6ecc34 100644 --- a/app/screens/home/search/search.tsx +++ b/app/screens/home/search/search.tsx @@ -240,6 +240,7 @@ const SearchScreen = ({teamId, teams}: Props) => { scrollEnabled={scrollEnabled} searchValue={searchValue} setRecentValue={handleRecentSearch} + searchRef={searchRef} setSearchValue={handleModifierTextChange} setTeamId={setSearchTeamId} teamId={searchTeamId} diff --git a/patches/react-native-elements+3.4.2.patch b/patches/react-native-elements+3.4.2.patch index 90f39bc6e9..e156152b6e 100644 --- a/patches/react-native-elements+3.4.2.patch +++ b/patches/react-native-elements+3.4.2.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/react-native-elements/dist/searchbar/SearchBar-android.js b/node_modules/react-native-elements/dist/searchbar/SearchBar-android.js -index 1bfd2b4..436e870 100644 +index 1bfd2b4..a4f6f77 100644 --- a/node_modules/react-native-elements/dist/searchbar/SearchBar-android.js +++ b/node_modules/react-native-elements/dist/searchbar/SearchBar-android.js @@ -10,7 +10,7 @@ var __rest = (this && this.__rest) || function (s, e) { @@ -11,7 +11,7 @@ index 1bfd2b4..436e870 100644 import { renderNode } from '../helpers'; import Input from '../input/Input'; import Icon from '../icons/Icon'; -@@ -68,24 +68,17 @@ class SearchBar extends Component { +@@ -68,24 +68,20 @@ class SearchBar extends Component { }; this.onBlur = (event) => { this.props.onBlur(event); @@ -24,7 +24,9 @@ index 1bfd2b4..436e870 100644 }; - this._keyboardDidHide = () => { - this.cancel(); -- }; ++ this.setNativeProps = (nativeProps: object) => { ++ this.input.setNativeProps && this.input.setNativeProps(nativeProps); + }; const { value = '' } = props; this.state = { hasFocus: false, @@ -38,10 +40,20 @@ index 1bfd2b4..436e870 100644 render() { var _a; diff --git a/node_modules/react-native-elements/dist/searchbar/SearchBar-ios.js b/node_modules/react-native-elements/dist/searchbar/SearchBar-ios.js -index 8fe90be..3daf517 100644 +index 8fe90be..69ab1ff 100644 --- a/node_modules/react-native-elements/dist/searchbar/SearchBar-ios.js +++ b/node_modules/react-native-elements/dist/searchbar/SearchBar-ios.js -@@ -85,6 +85,11 @@ class SearchBar extends Component { +@@ -78,6 +78,9 @@ class SearchBar extends Component { + this.props.onChangeText(text); + this.setState({ isEmpty: text === '' }); + }; ++ this.setNativeProps = (nativeProps: object) => { ++ this.input.setNativeProps && this.input.setNativeProps(nativeProps); ++ }; + const { value } = props; + this.state = { + hasFocus: false, +@@ -85,6 +88,11 @@ class SearchBar extends Component { cancelButtonWidth: null, }; } @@ -53,3 +65,29 @@ index 8fe90be..3daf517 100644 render() { var _a, _b, _c, _d, _e, _f, _g; const _h = this.props, { theme, cancelButtonProps, cancelButtonTitle, clearIcon, containerStyle, leftIconContainerStyle, rightIconContainerStyle, inputContainerStyle, inputStyle, placeholderTextColor, showLoading, loadingProps, searchIcon, showCancel } = _h, attributes = __rest(_h, ["theme", "cancelButtonProps", "cancelButtonTitle", "clearIcon", "containerStyle", "leftIconContainerStyle", "rightIconContainerStyle", "inputContainerStyle", "inputStyle", "placeholderTextColor", "showLoading", "loadingProps", "searchIcon", "showCancel"]); +diff --git a/node_modules/react-native-elements/dist/searchbar/SearchBar.d.ts b/node_modules/react-native-elements/dist/searchbar/SearchBar.d.ts +index 61844a8..a22a37c 100644 +--- a/node_modules/react-native-elements/dist/searchbar/SearchBar.d.ts ++++ b/node_modules/react-native-elements/dist/searchbar/SearchBar.d.ts +@@ -21,6 +21,7 @@ export declare type SearchBarBaseProps = React.ComponentPropsWithRef void; + }; + export declare type SearchBarProps = SearchBarBaseProps & SearchBarDefaultProps & SearchBarAndroidProps & SearchBarIosProps; + declare class SearchBar extends React.Component>> { +diff --git a/node_modules/react-native-elements/dist/searchbar/SearchBar.js b/node_modules/react-native-elements/dist/searchbar/SearchBar.js +index 43db650..6e8ebf5 100644 +--- a/node_modules/react-native-elements/dist/searchbar/SearchBar.js ++++ b/node_modules/react-native-elements/dist/searchbar/SearchBar.js +@@ -23,6 +23,9 @@ class SearchBar extends React.Component { + this.cancel = () => { + this.searchbar.cancel && this.searchbar.cancel(); + }; ++ this.setNativeProps = (nativeProps: object) => { ++ this.searchbar.setNativeProps && this.searchbar.setNativeProps(nativeProps); ++ }; + } + render() { + const Component = SEARCHBAR_COMPONENTS[this.props.platform] || DefaultSearchBar;