forked from Ivasoft/mattermost-mobile
[Gekidou MM-44940] Add autocomplete to navigation search (#6516)
* implement so that autocomplete will track the collapsable header * add search screen * move autocomplete to it's own view and add zindex to the view so it sits atop the modifiers and results * remove unused imports * - remove console.log - paddingTop is optional * remove comments * remove commented code * - use a more reasonable zindex value. On more than in Nav Header - use consts for autocomplete padding top * remvoe search screen button * - add dependencies * - remove search button * - not needed * move autocomplete zindex to constant * - move autocomplete directly below NavigationHeader - adjust distance from search box * - remove setXX from dependencies - remove useMemo because only returning the component * remove these changes. after placing autocomplete correcting in the search component, this works as expected * without this setting, ios has the issue where autocomplete is open, transparent, and has height. The user cannot click any modifiers. * - need usememot for autocomplete - add isSearch to remote runSearch function as option to hit the search for autocomplete API option - Initial component needs handleTextChange callback to set the cursorPosition correctly * use ternary operator
This commit is contained in:
@@ -1147,7 +1147,7 @@ export async function switchToLastChannel(serverUrl: string, teamId?: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function searchChannels(serverUrl: string, term: string) {
|
||||
export async function searchChannels(serverUrl: string, term: string, isSearch = false) {
|
||||
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
|
||||
if (!database) {
|
||||
return {error: `${serverUrl} database not found`};
|
||||
@@ -1162,7 +1162,8 @@ export async function searchChannels(serverUrl: string, term: string) {
|
||||
|
||||
try {
|
||||
const currentTeamId = await getCurrentTeamId(database);
|
||||
const channels = await client.autocompleteChannels(currentTeamId, term);
|
||||
const autoCompleteFunc = isSearch ? client.autocompleteChannelsForSearch : client.autocompleteChannels;
|
||||
const channels = await autoCompleteFunc(currentTeamId, term);
|
||||
return {channels};
|
||||
} catch (error) {
|
||||
return {error};
|
||||
|
||||
@@ -55,6 +55,7 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
type Props = {
|
||||
cursorPosition: number;
|
||||
postInputTop: number;
|
||||
paddingTop?: number;
|
||||
rootId?: string;
|
||||
channelId?: string;
|
||||
fixedBottomPosition?: boolean;
|
||||
@@ -72,6 +73,7 @@ type Props = {
|
||||
const Autocomplete = ({
|
||||
cursorPosition,
|
||||
postInputTop,
|
||||
paddingTop,
|
||||
rootId,
|
||||
channelId,
|
||||
isSearch = false,
|
||||
@@ -124,10 +126,10 @@ const Autocomplete = ({
|
||||
s.push(style.shadow);
|
||||
}
|
||||
if (isSearch) {
|
||||
s.push(style.base, style.searchContainer, {height: maxListHeight});
|
||||
s.push(style.base, paddingTop ? {top: paddingTop} : style.searchContainer, {maxHeight: maxListHeight});
|
||||
}
|
||||
return s;
|
||||
}, [style, isSearch && maxListHeight, hasElements]);
|
||||
}, [style, isSearch && maxListHeight, paddingTop]);
|
||||
|
||||
const containerStyles = useMemo(() => {
|
||||
const s = [];
|
||||
|
||||
@@ -218,7 +218,7 @@ const ChannelMention = ({
|
||||
|
||||
const runSearch = useMemo(() => debounce(async (sUrl: string, term: string) => {
|
||||
setLoading(true);
|
||||
const {channels: receivedChannels, error} = await searchChannels(sUrl, term);
|
||||
const {channels: receivedChannels, error} = await searchChannels(sUrl, term, isSearch);
|
||||
setUseLocal(Boolean(error));
|
||||
|
||||
if (error) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import {Edge, SafeAreaView} from 'react-native-safe-area-context';
|
||||
|
||||
import {addSearchToTeamSearchHistory} from '@actions/local/team';
|
||||
import {searchPosts, searchFiles} from '@actions/remote/search';
|
||||
import Autocomplete from '@components/autocomplete';
|
||||
import FreezeScreen from '@components/freeze_screen';
|
||||
import Loading from '@components/loading';
|
||||
import NavigationHeader from '@components/navigation_header';
|
||||
@@ -33,6 +34,9 @@ const emptyChannelIds: string[] = [];
|
||||
|
||||
const dummyData = [1];
|
||||
|
||||
const AutocompletePaddingTop = -4;
|
||||
const AutocompleteZindex = 11;
|
||||
|
||||
type Props = {
|
||||
teamId: string;
|
||||
}
|
||||
@@ -69,6 +73,7 @@ const SearchScreen = ({teamId}: Props) => {
|
||||
const serverUrl = useServerUrl();
|
||||
const searchTerm = (nav.getState().routes[stateIndex].params as any)?.searchTerm;
|
||||
|
||||
const [cursorPosition, setCursorPosition] = useState(searchTerm?.length);
|
||||
const [searchValue, setSearchValue] = useState<string>(searchTerm);
|
||||
const [searchTeamId, setSearchTeamId] = useState<string>(teamId);
|
||||
const [selectedTab, setSelectedTab] = useState<TabType>(TabTypes.MESSAGES);
|
||||
@@ -88,11 +93,16 @@ const SearchScreen = ({teamId}: Props) => {
|
||||
|
||||
const {scrollPaddingTop, scrollRef, scrollValue, onScroll, headerHeight, hideHeader} = useCollapsibleHeader<FlatList>(true, onSnap);
|
||||
|
||||
const handleTextChange = useCallback((newValue: string) => {
|
||||
setSearchValue(newValue);
|
||||
setCursorPosition(newValue.length);
|
||||
}, []);
|
||||
|
||||
const handleClearSearch = useCallback(() => {
|
||||
setSearchValue('');
|
||||
handleTextChange('');
|
||||
setLastSearchedValue('');
|
||||
setFilter(FileFilters.ALL);
|
||||
}, []);
|
||||
}, [handleTextChange]);
|
||||
|
||||
const handleCancelSearch = useCallback(() => {
|
||||
handleClearSearch();
|
||||
@@ -127,9 +137,9 @@ const SearchScreen = ({teamId}: Props) => {
|
||||
}, [handleSearch, searchTeamId, searchValue]);
|
||||
|
||||
const handleRecentSearch = useCallback((text: string) => {
|
||||
setSearchValue(text);
|
||||
handleTextChange(text);
|
||||
handleSearch(searchTeamId, text);
|
||||
}, [handleSearch, searchTeamId]);
|
||||
}, [handleSearch, handleTextChange, searchTeamId]);
|
||||
|
||||
const handleFilterChange = useCallback(async (filterValue: FileFilter) => {
|
||||
setLoading(true);
|
||||
@@ -159,11 +169,11 @@ const SearchScreen = ({teamId}: Props) => {
|
||||
<Initial
|
||||
searchValue={searchValue}
|
||||
setRecentValue={handleRecentSearch}
|
||||
setSearchValue={setSearchValue}
|
||||
setSearchValue={handleTextChange}
|
||||
setTeamId={setSearchTeamId}
|
||||
teamId={searchTeamId}
|
||||
/>
|
||||
), [searchValue, searchTeamId, handleRecentSearch]);
|
||||
), [searchValue, searchTeamId, handleRecentSearch, handleTextChange]);
|
||||
|
||||
const resultsComponent = useMemo(() => (
|
||||
<Results
|
||||
@@ -229,6 +239,17 @@ const SearchScreen = ({teamId}: Props) => {
|
||||
/>
|
||||
);
|
||||
}
|
||||
const autocomplete = useMemo(() => (
|
||||
<Autocomplete
|
||||
paddingTop={AutocompletePaddingTop}
|
||||
postInputTop={0}
|
||||
updateValue={handleTextChange}
|
||||
cursorPosition={cursorPosition}
|
||||
value={searchValue}
|
||||
isSearch={true}
|
||||
hasFilesAttached={false}
|
||||
/>
|
||||
), [cursorPosition, handleTextChange, searchValue]);
|
||||
|
||||
return (
|
||||
<FreezeScreen freeze={!isFocused}>
|
||||
@@ -239,7 +260,7 @@ const SearchScreen = ({teamId}: Props) => {
|
||||
hasSearch={true}
|
||||
scrollValue={scrollValue}
|
||||
hideHeader={hideHeader}
|
||||
onChangeText={setSearchValue}
|
||||
onChangeText={handleTextChange}
|
||||
onSubmitEditing={onSubmit}
|
||||
blurOnSubmit={true}
|
||||
placeholder={intl.formatMessage({id: 'screen.search.placeholder', defaultMessage: 'Search messages & files'})}
|
||||
@@ -247,6 +268,9 @@ const SearchScreen = ({teamId}: Props) => {
|
||||
onCancel={handleCancelSearch}
|
||||
defaultValue={searchValue}
|
||||
/>
|
||||
<Animated.View style={[top, {zIndex: AutocompleteZindex}]}>
|
||||
{autocomplete}
|
||||
</Animated.View>
|
||||
<SafeAreaView
|
||||
style={styles.flex}
|
||||
edges={EDGES}
|
||||
|
||||
Reference in New Issue
Block a user