import React, { FC, memo, useCallback, useEffect, useRef, useState, } from '../../../lib/teact/teact'; import { withGlobal } from '../../../lib/teact/teactn'; import { GlobalActions } from '../../../global/types'; import { ApiBotInlineMediaResult, ApiBotInlineResult, ApiBotInlineSwitchPm } from '../../../api/types'; import { IAllowedAttachmentOptions } from '../../../modules/helpers'; import { LoadMoreDirection } from '../../../types'; import { IS_TOUCH_ENV } from '../../../util/environment'; import setTooltipItemVisible from '../../../util/setTooltipItemVisible'; import buildClassName from '../../../util/buildClassName'; import captureKeyboardListeners from '../../../util/captureKeyboardListeners'; import cycleRestrict from '../../../util/cycleRestrict'; import useShowTransition from '../../../hooks/useShowTransition'; import { throttle } from '../../../util/schedulers'; import { pick } from '../../../util/iteratees'; import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver'; import usePrevious from '../../../hooks/usePrevious'; import MediaResult from './inlineResults/MediaResult'; import ArticleResult from './inlineResults/ArticleResult'; import GifResult from './inlineResults/GifResult'; import StickerResult from './inlineResults/StickerResult'; import ListItem from '../../ui/ListItem'; import InfiniteScroll from '../../ui/InfiniteScroll'; import './InlineBotTooltip.scss'; const INTERSECTION_DEBOUNCE_MS = 200; const runThrottled = throttle((cb) => cb(), 500, true); export type OwnProps = { isOpen: boolean; botId?: number; isGallery?: boolean; allowedAttachmentOptions: IAllowedAttachmentOptions; inlineBotResults?: (ApiBotInlineResult | ApiBotInlineMediaResult)[]; switchPm?: ApiBotInlineSwitchPm; onSelectResult: (inlineResult: ApiBotInlineMediaResult | ApiBotInlineResult) => void; loadMore: NoneToVoidFunction; onClose: NoneToVoidFunction; }; type DispatchProps = Pick; const InlineBotTooltip: FC = ({ isOpen, botId, isGallery, inlineBotResults, switchPm, loadMore, onClose, openChat, sendBotCommand, onSelectResult, }) => { // eslint-disable-next-line no-null/no-null const containerRef = useRef(null); const { shouldRender, transitionClassNames } = useShowTransition(isOpen, undefined, undefined, false); const [selectedIndex, setSelectedIndex] = useState(-1); const { observe: observeIntersection, } = useIntersectionObserver({ rootRef: containerRef, debounceMs: INTERSECTION_DEBOUNCE_MS, isDisabled: !isOpen, }); useEffect(() => { setSelectedIndex(isGallery ? -1 : 0); }, [inlineBotResults, isGallery]); useEffect(() => { setTooltipItemVisible('.chat-item-clickable', selectedIndex, containerRef); }, [selectedIndex]); const getSelectedIndex = useCallback((newIndex: number) => { if (!inlineBotResults || !inlineBotResults.length) { return -1; } return cycleRestrict(inlineBotResults.length, newIndex); }, [inlineBotResults]); const handleArrowKey = useCallback((value: number, e: KeyboardEvent) => { if (isGallery) { return; } e.preventDefault(); setSelectedIndex((index) => (getSelectedIndex(index + value))); }, [isGallery, getSelectedIndex]); const handleSelectInlineBotResult = useCallback((e: KeyboardEvent) => { if (inlineBotResults && inlineBotResults.length && selectedIndex > -1) { const inlineResult = inlineBotResults[selectedIndex]; if (inlineResult) { e.preventDefault(); onSelectResult(inlineResult); } } }, [inlineBotResults, onSelectResult, selectedIndex]); const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => { if (direction === LoadMoreDirection.Backwards) { runThrottled(loadMore); } }, [loadMore]); useEffect(() => (isOpen ? captureKeyboardListeners({ onEsc: onClose, onUp: (e: KeyboardEvent) => handleArrowKey(-1, e), onDown: (e: KeyboardEvent) => handleArrowKey(1, e), onEnter: handleSelectInlineBotResult, }) : undefined), [handleArrowKey, handleSelectInlineBotResult, isGallery, isOpen, onClose]); const handleSendPm = useCallback(() => { openChat({ id: botId }); sendBotCommand({ chatId: botId, command: `/start ${switchPm!.startParam}` }); }, [botId, openChat, sendBotCommand, switchPm]); const prevInlineBotResults = usePrevious( inlineBotResults && inlineBotResults.length ? inlineBotResults : undefined, shouldRender, ); const renderedInlineBotResults = inlineBotResults && !inlineBotResults.length ? prevInlineBotResults : inlineBotResults; if (!shouldRender || !renderedInlineBotResults || (!renderedInlineBotResults.length && !switchPm)) { return undefined; } const className = buildClassName( 'InlineBotTooltip composer-tooltip', IS_TOUCH_ENV ? 'no-scrollbar' : 'custom-scroll', isGallery && 'gallery', transitionClassNames, ); function renderSwitchPm() { return ( {switchPm!.text} ); } function renderContent() { return renderedInlineBotResults!.map((inlineBotResult, index) => { switch (inlineBotResult.type) { case 'gif': return ( ); case 'photo': return ( ); case 'sticker': return ( ); case 'video': case 'game': return ( ); case 'article': case 'audio': return ( ); default: return undefined; } }); } return ( {switchPm && renderSwitchPm()} {renderContent()} ); }; export default memo(withGlobal( undefined, (setGlobal, actions): DispatchProps => pick(actions, [ 'sendBotCommand', 'openChat', 'sendInlineBotResult', ]), )(InlineBotTooltip));