Mention Tooltip: Support catching "@" in the middle of input (#2131)
This commit is contained in:
parent
99767907a7
commit
a2a0161cc9
@ -104,9 +104,8 @@ const AttachmentModal: FC<OwnProps> = ({
|
|||||||
isMentionTooltipOpen, closeMentionTooltip, insertMention, mentionFilteredUsers,
|
isMentionTooltipOpen, closeMentionTooltip, insertMention, mentionFilteredUsers,
|
||||||
} = useMentionTooltip(
|
} = useMentionTooltip(
|
||||||
isOpen,
|
isOpen,
|
||||||
captionRef,
|
`#${EDITABLE_INPUT_MODAL_ID}`,
|
||||||
onCaptionUpdate,
|
onCaptionUpdate,
|
||||||
EDITABLE_INPUT_MODAL_ID,
|
|
||||||
groupChatMembers,
|
groupChatMembers,
|
||||||
undefined,
|
undefined,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
|
|||||||
@ -406,9 +406,8 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
isMentionTooltipOpen, closeMentionTooltip, insertMention, mentionFilteredUsers,
|
isMentionTooltipOpen, closeMentionTooltip, insertMention, mentionFilteredUsers,
|
||||||
} = useMentionTooltip(
|
} = useMentionTooltip(
|
||||||
!attachments.length,
|
!attachments.length,
|
||||||
htmlRef,
|
EDITABLE_INPUT_CSS_SELECTOR,
|
||||||
setHtml,
|
setHtml,
|
||||||
undefined,
|
|
||||||
groupChatMembers,
|
groupChatMembers,
|
||||||
topInlineBotIds,
|
topInlineBotIds,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
|
|||||||
@ -5,14 +5,17 @@ import { getGlobal } from '../../../../global';
|
|||||||
|
|
||||||
import type { ApiChatMember, ApiUser } from '../../../../api/types';
|
import type { ApiChatMember, ApiUser } from '../../../../api/types';
|
||||||
import { ApiMessageEntityTypes } from '../../../../api/types';
|
import { ApiMessageEntityTypes } from '../../../../api/types';
|
||||||
import { EDITABLE_INPUT_ID } from '../../../../config';
|
|
||||||
import { filterUsersByName, getUserFirstOrLastName } from '../../../../global/helpers';
|
import { filterUsersByName, getUserFirstOrLastName } from '../../../../global/helpers';
|
||||||
import { prepareForRegExp } from '../helpers/prepareForRegExp';
|
import { prepareForRegExp } from '../helpers/prepareForRegExp';
|
||||||
import focusEditableElement from '../../../../util/focusEditableElement';
|
import focusEditableElement from '../../../../util/focusEditableElement';
|
||||||
import { pickTruthy, unique } from '../../../../util/iteratees';
|
import { pickTruthy, unique } from '../../../../util/iteratees';
|
||||||
import { throttle } from '../../../../util/schedulers';
|
import { throttle } from '../../../../util/schedulers';
|
||||||
|
import { getHtmlBeforeSelection } from '../../../../util/selection';
|
||||||
|
|
||||||
import useFlag from '../../../../hooks/useFlag';
|
import useFlag from '../../../../hooks/useFlag';
|
||||||
|
import useCacheBuster from '../../../../hooks/useCacheBuster';
|
||||||
|
import useOnSelectionChange from '../../../../hooks/useOnSelectionChange';
|
||||||
|
|
||||||
const runThrottled = throttle((cb) => cb(), 500, true);
|
const runThrottled = throttle((cb) => cb(), 500, true);
|
||||||
let RE_USERNAME_SEARCH: RegExp;
|
let RE_USERNAME_SEARCH: RegExp;
|
||||||
@ -26,14 +29,14 @@ try {
|
|||||||
|
|
||||||
export default function useMentionTooltip(
|
export default function useMentionTooltip(
|
||||||
canSuggestMembers: boolean | undefined,
|
canSuggestMembers: boolean | undefined,
|
||||||
htmlRef: { current: string },
|
inputSelector: string,
|
||||||
onUpdateHtml: (html: string) => void,
|
onUpdateHtml: (html: string) => void,
|
||||||
inputId: string = EDITABLE_INPUT_ID,
|
|
||||||
groupChatMembers?: ApiChatMember[],
|
groupChatMembers?: ApiChatMember[],
|
||||||
topInlineBotIds?: string[],
|
topInlineBotIds?: string[],
|
||||||
currentUserId?: string,
|
currentUserId?: string,
|
||||||
) {
|
) {
|
||||||
const [isOpen, markIsOpen, unmarkIsOpen] = useFlag();
|
const [isOpen, markIsOpen, unmarkIsOpen] = useFlag();
|
||||||
|
const [htmlBeforeSelection, setHtmlBeforeSelection] = useState('');
|
||||||
const [usersToMention, setUsersToMention] = useState<ApiUser[] | undefined>();
|
const [usersToMention, setUsersToMention] = useState<ApiUser[] | undefined>();
|
||||||
|
|
||||||
const updateFilteredUsers = useCallback((filter, withInlineBots: boolean) => {
|
const updateFilteredUsers = useCallback((filter, withInlineBots: boolean) => {
|
||||||
@ -64,22 +67,35 @@ export default function useMentionTooltip(
|
|||||||
});
|
});
|
||||||
}, [currentUserId, groupChatMembers, topInlineBotIds]);
|
}, [currentUserId, groupChatMembers, topInlineBotIds]);
|
||||||
|
|
||||||
const html = htmlRef.current;
|
const [cacheBuster, updateCacheBuster] = useCacheBuster();
|
||||||
|
|
||||||
|
const handleSelectionChange = useCallback((range: Range) => {
|
||||||
|
if (range.collapsed) {
|
||||||
|
updateCacheBuster(); // Update tooltip on cursor move
|
||||||
|
}
|
||||||
|
}, [updateCacheBuster]);
|
||||||
|
|
||||||
|
useOnSelectionChange(document.querySelector<HTMLDivElement>(inputSelector), handleSelectionChange);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!canSuggestMembers || !html.length) {
|
setHtmlBeforeSelection(getHtmlBeforeSelection(document.querySelector<HTMLDivElement>(inputSelector)!));
|
||||||
|
}, [inputSelector, cacheBuster]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!canSuggestMembers || !htmlBeforeSelection.length) {
|
||||||
unmarkIsOpen();
|
unmarkIsOpen();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const usernameFilter = html.includes('@') && getUsernameFilter(html);
|
const usernameFilter = htmlBeforeSelection.includes('@') && getUsernameFilter(htmlBeforeSelection);
|
||||||
|
|
||||||
if (usernameFilter) {
|
if (usernameFilter) {
|
||||||
const filter = usernameFilter ? usernameFilter.substr(1) : '';
|
const filter = usernameFilter ? usernameFilter.substr(1) : '';
|
||||||
updateFilteredUsers(filter, canSuggestInlineBots(html));
|
updateFilteredUsers(filter, canSuggestInlineBots(htmlBeforeSelection));
|
||||||
} else {
|
} else {
|
||||||
unmarkIsOpen();
|
unmarkIsOpen();
|
||||||
}
|
}
|
||||||
}, [canSuggestMembers, updateFilteredUsers, markIsOpen, unmarkIsOpen, html]);
|
}, [canSuggestMembers, updateFilteredUsers, markIsOpen, unmarkIsOpen, htmlBeforeSelection]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (usersToMention?.length) {
|
if (usersToMention?.length) {
|
||||||
@ -104,18 +120,21 @@ export default function useMentionTooltip(
|
|||||||
dir="auto"
|
dir="auto"
|
||||||
>${getUserFirstOrLastName(user)}</a>`;
|
>${getUserFirstOrLastName(user)}</a>`;
|
||||||
|
|
||||||
const currentHtml = htmlRef.current;
|
const containerEl = document.querySelector<HTMLDivElement>(inputSelector)!;
|
||||||
const atIndex = currentHtml.lastIndexOf('@');
|
|
||||||
|
const atIndex = htmlBeforeSelection.lastIndexOf('@');
|
||||||
if (atIndex !== -1) {
|
if (atIndex !== -1) {
|
||||||
onUpdateHtml(`${currentHtml.substr(0, atIndex)}${insertedHtml} `);
|
const newHtml = `${htmlBeforeSelection.substr(0, atIndex)}${insertedHtml} `;
|
||||||
const messageInput = document.getElementById(inputId)!;
|
const htmlAfterSelection = containerEl.innerHTML.substring(htmlBeforeSelection.length);
|
||||||
|
onUpdateHtml(`${newHtml}${htmlAfterSelection}`);
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
focusEditableElement(messageInput, forceFocus);
|
focusEditableElement(containerEl, forceFocus);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
unmarkIsOpen();
|
unmarkIsOpen();
|
||||||
}, [htmlRef, inputId, onUpdateHtml, unmarkIsOpen]);
|
}, [htmlBeforeSelection, inputSelector, onUpdateHtml, unmarkIsOpen]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isMentionTooltipOpen: isOpen,
|
isMentionTooltipOpen: isOpen,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user