Emoji Tooltip: Disable tooltip for a single character (#1178)
This commit is contained in:
parent
824c2f684a
commit
3eb8d6259f
@ -33,6 +33,7 @@ export type OwnProps = {
|
||||
usersById?: Record<number, ApiUser>;
|
||||
recentEmojis: string[];
|
||||
language: LangCode;
|
||||
emojiKeywords?: Record<string, string[]>;
|
||||
addRecentEmoji: AnyToVoidFunction;
|
||||
loadEmojiKeywords: AnyToVoidFunction;
|
||||
onCaptionUpdate: (html: string) => void;
|
||||
@ -52,6 +53,7 @@ const AttachmentModal: FC<OwnProps> = ({
|
||||
usersById,
|
||||
recentEmojis,
|
||||
language,
|
||||
emojiKeywords,
|
||||
onCaptionUpdate,
|
||||
addRecentEmoji,
|
||||
loadEmojiKeywords,
|
||||
@ -89,6 +91,7 @@ const AttachmentModal: FC<OwnProps> = ({
|
||||
recentEmojis,
|
||||
EDITABLE_INPUT_MODAL_ID,
|
||||
onCaptionUpdate,
|
||||
emojiKeywords,
|
||||
);
|
||||
|
||||
useEffect(() => (isOpen ? captureEscKeyListener(onClear) : undefined), [isOpen, onClear]);
|
||||
|
||||
@ -673,6 +673,10 @@ const Composer: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
: (isSymbolMenuOpen && 'is-loading'),
|
||||
);
|
||||
|
||||
const onSend = mainButtonState === MainButtonState.Edit
|
||||
? handleEditComplete
|
||||
: (shouldSchedule ? openCalendar : handleSend);
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
{allowedAttachmentOptions.canAttachMedia && (
|
||||
@ -695,6 +699,7 @@ const Composer: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
recentEmojis={recentEmojis}
|
||||
onCaptionUpdate={setHtml}
|
||||
language={language}
|
||||
emojiKeywords={emojiKeywords}
|
||||
addRecentEmoji={addRecentEmoji}
|
||||
loadEmojiKeywords={loadEmojiKeywords}
|
||||
onSend={shouldSchedule ? openCalendar : handleSend}
|
||||
@ -774,9 +779,7 @@ const Composer: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
shouldSupressFocus={IS_MOBILE_SCREEN && isSymbolMenuOpen}
|
||||
shouldSupressTextFormatter={isEmojiTooltipOpen || isMentionTooltipOpen}
|
||||
onUpdate={setHtml}
|
||||
onSend={mainButtonState === MainButtonState.Edit
|
||||
? handleEditComplete
|
||||
: (shouldSchedule ? openCalendar : handleSend)}
|
||||
onSend={onSend}
|
||||
onSupressedFocus={closeSymbolMenu}
|
||||
/>
|
||||
{withScheduledButton && (
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
&.focus,
|
||||
&:hover {
|
||||
background-color: var(--color-interactive-element-hover);
|
||||
background-color: var(--color-background-selected);
|
||||
}
|
||||
|
||||
& > img {
|
||||
|
||||
@ -2,6 +2,8 @@ import React, {
|
||||
FC, memo, useCallback, useEffect, useRef, useState,
|
||||
} from '../../../lib/teact/teact';
|
||||
|
||||
import { LangCode } from '../../../types';
|
||||
|
||||
import { IS_TOUCH_ENV } from '../../../util/environment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import cycleRestrict from '../../../util/cycleRestrict';
|
||||
@ -16,10 +18,11 @@ import Loading from '../../ui/Loading';
|
||||
import EmojiButton from './EmojiButton';
|
||||
|
||||
import './EmojiTooltip.scss';
|
||||
import { LangCode } from '../../../types';
|
||||
|
||||
const VIEWPORT_MARGIN = 8;
|
||||
const EMOJI_BUTTON_WIDTH = 44;
|
||||
const CLOSE_DURATION = 350;
|
||||
const NO_EMOJI_SELECTED_INDEX = -1;
|
||||
|
||||
function setItemVisible(index: number, containerRef: Record<string, any>) {
|
||||
const container = containerRef.current!;
|
||||
@ -59,8 +62,6 @@ export type OwnProps = {
|
||||
emojis: Emoji[];
|
||||
};
|
||||
|
||||
const CLOSE_DURATION = 350;
|
||||
|
||||
const EmojiTooltip: FC<OwnProps> = ({
|
||||
isOpen,
|
||||
language,
|
||||
@ -75,7 +76,7 @@ const EmojiTooltip: FC<OwnProps> = ({
|
||||
const { shouldRender, transitionClassNames } = useShowTransition(isOpen, undefined, undefined, false);
|
||||
const listEmojis: Emoji[] = usePrevDuringAnimation(emojis.length ? emojis : undefined, CLOSE_DURATION) || [];
|
||||
|
||||
const [selectedIndex, setSelectedIndex] = useState(-1);
|
||||
const [selectedIndex, setSelectedIndex] = useState(NO_EMOJI_SELECTED_INDEX);
|
||||
|
||||
useEffect(() => {
|
||||
loadEmojiKeywords({ language });
|
||||
@ -91,21 +92,20 @@ const EmojiTooltip: FC<OwnProps> = ({
|
||||
|
||||
const getSelectedIndex = useCallback((newIndex: number) => {
|
||||
if (!emojis.length) {
|
||||
return -1;
|
||||
return NO_EMOJI_SELECTED_INDEX;
|
||||
}
|
||||
|
||||
const emojisCount = emojis.length;
|
||||
return cycleRestrict(emojisCount, newIndex);
|
||||
}, [emojis]);
|
||||
|
||||
|
||||
const handleArrowKey = useCallback((value: number, e: KeyboardEvent) => {
|
||||
e.preventDefault();
|
||||
setSelectedIndex((index) => (getSelectedIndex(index + value)));
|
||||
}, [setSelectedIndex, getSelectedIndex]);
|
||||
|
||||
const handleSelectEmoji = useCallback((e: KeyboardEvent) => {
|
||||
if (emojis.length && selectedIndex > -1) {
|
||||
if (emojis.length && selectedIndex > NO_EMOJI_SELECTED_INDEX) {
|
||||
const emoji = emojis[selectedIndex];
|
||||
if (emoji) {
|
||||
e.preventDefault();
|
||||
|
||||
@ -17,8 +17,16 @@ let emojiDataPromise: Promise<EmojiModule>;
|
||||
let emojiRawData: EmojiRawData;
|
||||
let emojiData: EmojiData;
|
||||
|
||||
const RE_NOT_EMOJI_SEARCH = /[^-_:\p{L}\p{N}]+/iu;
|
||||
let RE_NOT_EMOJI_SEARCH: RegExp;
|
||||
const EMOJIS_LIMIT = 36;
|
||||
const FILTER_MIN_LENGTH = 2;
|
||||
|
||||
try {
|
||||
RE_NOT_EMOJI_SEARCH = new RegExp('[^-_:\\p{L}\\p{N}]+', 'iu');
|
||||
} catch (e) {
|
||||
// Support for older versions of firefox
|
||||
RE_NOT_EMOJI_SEARCH = new RegExp('[^-_:\\d\\wа-яё]+', 'i');
|
||||
}
|
||||
|
||||
export default function useEmojiTooltip(
|
||||
isAllowed: boolean,
|
||||
@ -31,7 +39,9 @@ export default function useEmojiTooltip(
|
||||
const [isOpen, markIsOpen, unmarkIsOpen] = useFlag();
|
||||
|
||||
const [byId, setById] = useState<Record<string, Emoji> | undefined>();
|
||||
const [keywords, setKeywords] = useState<string[]>();
|
||||
const [byKeyword, setByKeyword] = useState<Record<string, Emoji[]>>({});
|
||||
const [names, setNames] = useState<string[]>();
|
||||
const [byName, setByName] = useState<Record<string, Emoji[]>>({});
|
||||
|
||||
const [filteredEmojis, setFilteredEmojis] = useState<Emoji[]>([]);
|
||||
@ -70,12 +80,14 @@ export default function useEmojiTooltip(
|
||||
|
||||
if (emojiKeywords) {
|
||||
const byNative = buildCollectionByKey(emojis, 'native');
|
||||
setByKeyword(mapValues(emojiKeywords, (natives) => {
|
||||
const emojisByKeyword = mapValues(emojiKeywords, (natives) => {
|
||||
return Object.values(pickTruthy(byNative, natives));
|
||||
}));
|
||||
});
|
||||
setByKeyword(emojisByKeyword);
|
||||
setKeywords(Object.keys(emojisByKeyword));
|
||||
}
|
||||
|
||||
setByName(emojis.reduce((result, emoji) => {
|
||||
const emojisByName = emojis.reduce((result, emoji) => {
|
||||
emoji.names.forEach((name) => {
|
||||
if (!result[name]) {
|
||||
result[name] = [];
|
||||
@ -85,7 +97,9 @@ export default function useEmojiTooltip(
|
||||
});
|
||||
|
||||
return result;
|
||||
}, {} as Record<string, Emoji[]>));
|
||||
}, {} as Record<string, Emoji[]>);
|
||||
setByName(emojisByName);
|
||||
setNames(Object.keys(emojisByName));
|
||||
}, [byId, emojiKeywords]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -106,12 +120,12 @@ export default function useEmojiTooltip(
|
||||
|
||||
if (!filter) {
|
||||
matched = recentEmojis;
|
||||
} else {
|
||||
const matchedKeywords = Object.keys(byKeyword).filter((keyword) => keyword.startsWith(filter));
|
||||
} else if (filter.length >= FILTER_MIN_LENGTH) {
|
||||
const matchedKeywords = keywords.filter((keyword) => keyword.startsWith(filter)).sort();
|
||||
matched = matched.concat(flatten(Object.values(pickTruthy(byKeyword, matchedKeywords))));
|
||||
|
||||
// Also search by names, which is useful for non-English languages
|
||||
const matchedNames = Object.keys(byName).filter((name) => name.startsWith(filter));
|
||||
const matchedNames = names.filter((name) => name.startsWith(filter));
|
||||
matched = matched.concat(flatten(Object.values(pickTruthy(byName, matchedNames))));
|
||||
|
||||
matched = unique(matched);
|
||||
@ -123,7 +137,10 @@ export default function useEmojiTooltip(
|
||||
} else {
|
||||
unmarkIsOpen();
|
||||
}
|
||||
}, [byId, byKeyword, byName, html, isAllowed, markIsOpen, recentEmojis, unmarkIsOpen]);
|
||||
}, [
|
||||
byId, byKeyword, keywords, byName, names,
|
||||
html, isAllowed, markIsOpen, recentEmojis, unmarkIsOpen,
|
||||
]);
|
||||
|
||||
const insertEmoji = useCallback((textEmoji: string) => {
|
||||
const atIndex = html.lastIndexOf(':');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user