diff --git a/app/components/navigation_header/index.tsx b/app/components/navigation_header/index.tsx index 7b2183013a..365ee8c1f3 100644 --- a/app/components/navigation_header/index.tsx +++ b/app/components/navigation_header/index.tsx @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React from 'react'; +import React, {forwardRef} from 'react'; import Animated, {useAnimatedStyle, useDerivedValue} from 'react-native-reanimated'; import {SEARCH_INPUT_HEIGHT, SEARCH_INPUT_MARGIN} from '@constants/view'; @@ -14,7 +14,7 @@ import Header, {HeaderRightButton} from './header'; import NavigationHeaderLargeTitle from './large'; import NavigationSearch from './search'; -import type {SearchProps} from '@components/search'; +import type {SearchProps, SearchRef} from '@components/search'; type Props = SearchProps & { hasSearch?: boolean; @@ -41,7 +41,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ }, })); -const NavigationHeader = ({ +const NavigationHeader = forwardRef(({ hasSearch = false, isLargeTitle = false, leftComponent, @@ -56,7 +56,7 @@ const NavigationHeader = ({ title = '', hideHeader, ...searchProps -}: Props) => { +}: Props, ref) => { const theme = useTheme(); const styles = getStyleSheet(theme); @@ -125,12 +125,14 @@ const NavigationHeader = ({ hideHeader={hideHeader} theme={theme} topStyle={searchTopStyle} + ref={ref} /> } ); -}; +}); +NavigationHeader.displayName = 'NavHeader'; export default NavigationHeader; diff --git a/app/components/navigation_header/search.tsx b/app/components/navigation_header/search.tsx index 6cb20421f0..ccba4a3156 100644 --- a/app/components/navigation_header/search.tsx +++ b/app/components/navigation_header/search.tsx @@ -1,11 +1,11 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useCallback, useEffect, useMemo} from 'react'; +import React, {forwardRef, useCallback, useEffect, useMemo} from 'react'; import {DeviceEventEmitter, Keyboard, NativeSyntheticEvent, Platform, TextInputFocusEventData, ViewStyle} from 'react-native'; import Animated, {AnimatedStyleProp} from 'react-native-reanimated'; -import Search, {SearchProps} from '@components/search'; +import Search, {SearchProps, SearchRef} from '@components/search'; import {Events} from '@constants'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; @@ -31,12 +31,12 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ }, })); -const NavigationSearch = ({ +const NavigationSearch = forwardRef(({ hideHeader, theme, topStyle, ...searchProps -}: Props) => { +}: Props, ref) => { const styles = getStyleSheet(theme); const cancelButtonProps: SearchProps['cancelButtonProps'] = useMemo(() => ({ @@ -52,24 +52,27 @@ const NavigationSearch = ({ searchProps.onFocus?.(e); }, [hideHeader, searchProps.onFocus]); - useEffect(() => { - const show = Keyboard.addListener('keyboardDidShow', () => { - if (Platform.OS === 'android') { - DeviceEventEmitter.emit(Events.TAB_BAR_VISIBLE, false); - } - }); + const showEmitter = useCallback(() => { + if (Platform.OS === 'android') { + DeviceEventEmitter.emit(Events.TAB_BAR_VISIBLE, false); + } + }, []); - const hide = Keyboard.addListener('keyboardDidHide', () => { - if (Platform.OS === 'android') { - DeviceEventEmitter.emit(Events.TAB_BAR_VISIBLE, true); - } - }); + const hideEmitter = useCallback(() => { + if (Platform.OS === 'android') { + DeviceEventEmitter.emit(Events.TAB_BAR_VISIBLE, true); + } + }, []); + + useEffect(() => { + const show = Keyboard.addListener('keyboardDidShow', showEmitter); + const hide = Keyboard.addListener('keyboardDidHide', hideEmitter); return () => { hide.remove(); show.remove(); }; - }, []); + }, [hideEmitter, showEmitter]); return ( @@ -83,10 +86,12 @@ const NavigationSearch = ({ placeholderTextColor={changeOpacity(theme.sidebarText, Platform.select({android: 0.56, default: 0.72}))} searchIconColor={theme.sidebarText} selectionColor={theme.sidebarText} + ref={ref} /> ); -}; +}); +NavigationSearch.displayName = 'NavSearch'; export default NavigationSearch; diff --git a/app/components/search/index.tsx b/app/components/search/index.tsx index 24cfcab26f..756ff377d7 100644 --- a/app/components/search/index.tsx +++ b/app/components/search/index.tsx @@ -42,7 +42,7 @@ export type SearchProps = TextInputProps & { showLoading?: boolean; }; -type SearchRef = { +export type SearchRef = { blur: () => void; cancel: () => void; clear: () => void; @@ -151,7 +151,6 @@ const Search = forwardRef((props: SearchProps, ref) => { focus: () => { searchRef.current?.focus(); }, - }), [searchRef]); return ( diff --git a/app/screens/home/index.tsx b/app/screens/home/index.tsx index f612c643eb..e19df8e3e1 100644 --- a/app/screens/home/index.tsx +++ b/app/screens/home/index.tsx @@ -20,10 +20,9 @@ import Account from './account'; import ChannelList from './channel_list'; import RecentMentions from './recent_mentions'; import SavedMessages from './saved_messages'; +import Search from './search'; import TabBar from './tab_bar'; -// import Search from './search'; - import type {LaunchProps} from '@typings/launch'; if (Platform.OS === 'ios') { @@ -125,11 +124,11 @@ export default function HomeScreen(props: HomeProps) { > {() => } - {/* */} + /> { const clearRef = useRef(false); const cancelRef = useRef(false); + const searchRef = useRef(null); + const [cursorPosition, setCursorPosition] = useState(searchTerm?.length || 0); const [searchValue, setSearchValue] = useState(searchTerm || ''); const [searchTeamId, setSearchTeamId] = useState(teamId); @@ -144,6 +147,11 @@ const SearchScreen = ({teamId}: Props) => { setCursorPosition(newValue.length); }, []); + const handleModifierTextChange = useCallback((newValue: string) => { + searchRef.current?.focus?.(); + handleTextChange(newValue); + }, [handleTextChange]); + const handleLoading = useCallback((show: boolean) => { (showResults ? setResultsLoading : setLoading)(show); }, [showResults]); @@ -218,7 +226,7 @@ const SearchScreen = ({teamId}: Props) => { scrollEnabled={scrollEnabled} searchValue={searchValue} setRecentValue={handleRecentSearch} - setSearchValue={handleTextChange} + setSearchValue={handleModifierTextChange} setTeamId={setSearchTeamId} teamId={searchTeamId} /> @@ -318,6 +326,7 @@ const SearchScreen = ({teamId}: Props) => { onClear={handleClearSearch} onCancel={handleCancelSearch} defaultValue={searchValue} + ref={searchRef} />