From a219f6762479d66e2c6b8e7540d15c18b724630e Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Wed, 22 Feb 2023 23:48:42 +0100 Subject: [PATCH] Bot Command Tooltip: Fix sending wrong command --- .../middle/composer/BotCommandTooltip.tsx | 15 +++++++++++- src/components/middle/composer/Composer.tsx | 1 + .../composer/hooks/useBotCommandTooltip.ts | 2 +- .../composer/hooks/useKeyboardNavigation.ts | 9 +++++-- src/util/captureKeyboardListeners.ts | 24 ++++++++++++++----- 5 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/components/middle/composer/BotCommandTooltip.tsx b/src/components/middle/composer/BotCommandTooltip.tsx index 775b7ec70..5711f6e0e 100644 --- a/src/components/middle/composer/BotCommandTooltip.tsx +++ b/src/components/middle/composer/BotCommandTooltip.tsx @@ -4,6 +4,7 @@ import React, { } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; +import type { Signal } from '../../../util/signals'; import type { ApiBotCommand, ApiUser } from '../../../api/types'; import buildClassName from '../../../util/buildClassName'; @@ -20,6 +21,7 @@ export type OwnProps = { isOpen: boolean; withUsername?: boolean; botCommands?: ApiBotCommand[]; + getHtml: Signal; onClick: NoneToVoidFunction; onClose: NoneToVoidFunction; }; @@ -33,6 +35,7 @@ const BotCommandTooltip: FC = ({ isOpen, withUsername, botCommands, + getHtml, onClick, onClose, }) => { @@ -50,10 +53,20 @@ const BotCommandTooltip: FC = ({ onClick(); }, [onClick, sendBotCommand, usersById, withUsername]); + const handleSelect = useCallback((botCommand: ApiBotCommand) => { + // We need an additional check because tooltip is updated with throttling + if (!botCommand.command.startsWith(getHtml())) { + return false; + } + + handleSendCommand(botCommand); + return true; + }, [getHtml, handleSendCommand]); + const selectedCommandIndex = useKeyboardNavigation({ isActive: isOpen, items: botCommands, - onSelect: handleSendCommand, + onSelect: handleSelect, onClose, }); diff --git a/src/components/middle/composer/Composer.tsx b/src/components/middle/composer/Composer.tsx index 81532b34d..6853886ad 100644 --- a/src/components/middle/composer/Composer.tsx +++ b/src/components/middle/composer/Composer.tsx @@ -1267,6 +1267,7 @@ const Composer: FC = ({ isOpen={isBotCommandTooltipOpen} withUsername={Boolean(chatBotCommands)} botCommands={botTooltipCommands} + getHtml={getHtml} onClick={handleBotCommandSelect} onClose={closeBotCommandTooltip} /> diff --git a/src/components/middle/composer/hooks/useBotCommandTooltip.ts b/src/components/middle/composer/hooks/useBotCommandTooltip.ts index ed9dde28c..a4d7f0131 100644 --- a/src/components/middle/composer/hooks/useBotCommandTooltip.ts +++ b/src/components/middle/composer/hooks/useBotCommandTooltip.ts @@ -8,7 +8,7 @@ import useFlag from '../../../../hooks/useFlag'; import useDerivedSignal from '../../../../hooks/useDerivedSignal'; import { useThrottledResolver } from '../../../../hooks/useAsyncResolvers'; -const RE_COMMAND = /^\/([\w@]{1,32}\s?)?/i; +const RE_COMMAND = /^\/([\w@]{1,32})?$/i; const THROTTLE = 300; diff --git a/src/components/middle/composer/hooks/useKeyboardNavigation.ts b/src/components/middle/composer/hooks/useKeyboardNavigation.ts index b01a3e941..4d5a8845a 100644 --- a/src/components/middle/composer/hooks/useKeyboardNavigation.ts +++ b/src/components/middle/composer/hooks/useKeyboardNavigation.ts @@ -20,7 +20,7 @@ export function useKeyboardNavigation({ noArrowNavigation?: boolean; items?: any[]; shouldSelectOnTab?: boolean; - onSelect: AnyToVoidFunction; + onSelect: (item: any) => void | boolean; onClose: NoneToVoidFunction; }) { const [selectedItemIndex, setSelectedItemIndex] = useState(-1); @@ -42,10 +42,15 @@ export function useKeyboardNavigation({ if (items && items.length && selectedItemIndex > -1) { const item = items[selectedItemIndex]; if (item) { + if (onSelect(item) === false) { + return false; + } + e.preventDefault(); - onSelect(item); } } + + return true; }, [items, onSelect, selectedItemIndex]); const isSelectionOutOfRange = !items || selectedItemIndex > items.length - 1; diff --git a/src/util/captureKeyboardListeners.ts b/src/util/captureKeyboardListeners.ts index cef332fb3..d750f8b03 100644 --- a/src/util/captureKeyboardListeners.ts +++ b/src/util/captureKeyboardListeners.ts @@ -1,6 +1,14 @@ -type HandlerName = 'onEnter' | 'onBackspace' | 'onDelete' | 'onEsc' | 'onUp' | 'onDown' | 'onLeft' | 'onRight' -| 'onTab'; -type Handler = (e: KeyboardEvent) => void; +type HandlerName = + 'onEnter' + | 'onBackspace' + | 'onDelete' + | 'onEsc' + | 'onUp' + | 'onDown' + | 'onLeft' + | 'onRight' + | 'onTab'; +type Handler = (e: KeyboardEvent) => void | boolean; type CaptureOptions = Partial>; const keyToHandlerName: Record = { @@ -64,10 +72,14 @@ function handleKeyDown(e: KeyboardEvent) { if (!length) { return; } - e.stopPropagation(); - const handler = handlers[handlerName][length - 1]; - handler!(e); + for (let i = length - 1; i >= 0; i--) { + const handler = handlers[handlerName][i]!; + if (handler(e) !== false) { + e.stopPropagation(); + break; + } + } } function releaseKeyboardListener(options: CaptureOptions) {