// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import {Portal} from '@gorhom/portal'; import React, {useMemo, useState} from 'react'; import {Platform, type StyleProp, useWindowDimensions, type ViewStyle} from 'react-native'; import Animated, {type SharedValue, useAnimatedStyle, useDerivedValue} from 'react-native-reanimated'; import {MAX_LIST_HEIGHT, MAX_LIST_TABLET_DIFF} from '@constants/autocomplete'; import {useTheme} from '@context/theme'; import {useIsTablet} from '@hooks/device'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import AtMention from './at_mention/'; import ChannelMention from './channel_mention/'; import EmojiSuggestion from './emoji_suggestion/'; import SlashSuggestion from './slash_suggestion/'; import AppSlashSuggestion from './slash_suggestion/app_slash_suggestion/'; const getStyleFromTheme = makeStyleSheetFromTheme((theme) => { return { base: { position: 'absolute', }, borders: { borderWidth: 1, borderColor: changeOpacity(theme.centerChannelColor, 0.2), overflow: 'hidden', borderRadius: 8, elevation: 3, }, shadow: { backgroundColor: theme.centerChannelBg, shadowColor: '#000', shadowOpacity: 1, shadowRadius: 6, shadowOffset: { width: 0, height: 6, }, }, listStyle: { backgroundColor: theme.centerChannelBg, borderRadius: 4, paddingHorizontal: 16, }, }; }); type Props = { cursorPosition: number; position: SharedValue; rootId?: string; channelId?: string; isSearch?: boolean; value: string; enableDateSuggestion?: boolean; isAppsEnabled: boolean; nestedScrollEnabled?: boolean; updateValue: (v: string) => void; shouldDirectlyReact?: boolean; availableSpace: SharedValue; growDown?: boolean; teamId?: string; containerStyle?: StyleProp; autocompleteProviders?: AutocompleteProviders; useAllAvailableSpace?: boolean; horizontalPadding?: number; usePortal?: boolean; } type AutocompleteProviders = { user: boolean; channel: boolean; emoji: boolean; slash: boolean; } const defaultAutocompleteProviders: AutocompleteProviders = { user: true, channel: true, emoji: true, slash: true, }; const Autocomplete = ({ cursorPosition, position, rootId, channelId, isSearch = false, value, availableSpace, //enableDateSuggestion = false, isAppsEnabled, nestedScrollEnabled = false, updateValue, shouldDirectlyReact = false, growDown = false, containerStyle, teamId, autocompleteProviders = defaultAutocompleteProviders, useAllAvailableSpace = false, horizontalPadding = 8, usePortal = false, }: Props) => { const theme = useTheme(); const isTablet = useIsTablet(); const style = getStyleFromTheme(theme); const dimensions = useWindowDimensions(); const [showingAtMention, setShowingAtMention] = useState(false); const [showingChannelMention, setShowingChannelMention] = useState(false); const [showingEmoji, setShowingEmoji] = useState(false); const [showingCommand, setShowingCommand] = useState(false); const [showingAppCommand, setShowingAppCommand] = useState(false); // const [showingDate, setShowingDate] = useState(false); const hasElements = showingChannelMention || showingEmoji || showingAtMention || showingCommand || showingAppCommand; // || showingDate; const appsTakeOver = showingAppCommand; const showCommands = !(showingChannelMention || showingEmoji || showingAtMention); const isLandscape = dimensions.width > dimensions.height; const maxHeightAdjust = (isTablet && isLandscape) ? MAX_LIST_TABLET_DIFF : 0; const defaultMaxHeight = MAX_LIST_HEIGHT - maxHeightAdjust; const maxHeight = useDerivedValue(() => { return useAllAvailableSpace ? availableSpace.value : Math.min(availableSpace.value, defaultMaxHeight); }, [defaultMaxHeight, useAllAvailableSpace, availableSpace]); const containerAnimatedStyle = useAnimatedStyle(() => { return growDown ? {top: position.value, bottom: Platform.OS === 'ios' ? 'auto' : undefined, maxHeight: maxHeight.value} : {top: Platform.OS === 'ios' ? 'auto' : undefined, bottom: position.value, maxHeight: maxHeight.value}; }, [growDown, position]); const containerStyles = useMemo(() => { const s: StyleProp = [style.base, {left: horizontalPadding, right: horizontalPadding}, containerAnimatedStyle]; if (hasElements) { s.push(style.borders); } if (Platform.OS === 'ios') { s.push(style.shadow); } if (containerStyle) { s.push(containerStyle); } return s; }, [style.base, style.borders, style.shadow, horizontalPadding, containerAnimatedStyle, hasElements, containerStyle]); const component = ( {isAppsEnabled && channelId && autocompleteProviders.slash && ( )} {(!appsTakeOver || !isAppsEnabled) && (<> {autocompleteProviders.user && ( )} {autocompleteProviders.channel && ( )} {!isSearch && autocompleteProviders.emoji && ( )} {showCommands && channelId && autocompleteProviders.slash && ( )} {/* {(isSearch && enableDateSuggestion) && } */} )} ); if (usePortal && Platform.OS === 'android') { return ( {component} ); } return component; }; export default Autocomplete;