Emoji Tooltip: Various fixes (#2688)
This commit is contained in:
parent
92c1b0b281
commit
7c7e0c1d69
@ -2,6 +2,7 @@ import React, { memo, useCallback } from '../../../lib/teact/teact';
|
||||
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import type { ApiSticker } from '../../../api/types';
|
||||
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
||||
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
|
||||
@ -15,10 +16,11 @@ type OwnProps = {
|
||||
emoji: ApiSticker;
|
||||
focus?: boolean;
|
||||
onClick?: (emoji: ApiSticker) => void;
|
||||
observeIntersection?: ObserveFn;
|
||||
};
|
||||
|
||||
const CustomEmojiButton: FC<OwnProps> = ({
|
||||
emoji, focus, onClick,
|
||||
emoji, focus, onClick, observeIntersection,
|
||||
}) => {
|
||||
const handleClick = useCallback((e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
// Preventing safari from losing focus on Composer MessageInput
|
||||
@ -38,7 +40,13 @@ const CustomEmojiButton: FC<OwnProps> = ({
|
||||
onMouseDown={handleClick}
|
||||
title={emoji.emoji}
|
||||
>
|
||||
<CustomEmoji documentId={emoji.id} size={CUSTOM_EMOJI_SIZE} withSharedAnimation shouldPreloadPreview />
|
||||
<CustomEmoji
|
||||
documentId={emoji.id}
|
||||
size={CUSTOM_EMOJI_SIZE}
|
||||
withSharedAnimation
|
||||
shouldPreloadPreview
|
||||
observeIntersectionForPlaying={observeIntersection}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
}
|
||||
|
||||
.emojiButton {
|
||||
flex: 0 0 2.5rem;
|
||||
flex: 0 0 2rem;
|
||||
--custom-emoji-size: 2rem;
|
||||
margin: 0.5rem 0 0.5rem 0.25rem;
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ const CustomEmojiTooltip: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const {
|
||||
observe: observeIntersection,
|
||||
} = useIntersectionObserver({ rootRef: containerRef, throttleMs: INTERSECTION_THROTTLE });
|
||||
} = useIntersectionObserver({ rootRef: containerRef, throttleMs: INTERSECTION_THROTTLE, isDisabled: !isOpen });
|
||||
|
||||
useEffect(() => (isOpen ? captureEscKeyListener(onClose) : undefined), [isOpen, onClose]);
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ import useShowTransition from '../../../hooks/useShowTransition';
|
||||
import usePrevDuringAnimation from '../../../hooks/usePrevDuringAnimation';
|
||||
import { useKeyboardNavigation } from './hooks/useKeyboardNavigation';
|
||||
import useHorizontalScroll from '../../../hooks/useHorizontalScroll';
|
||||
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
|
||||
|
||||
import Loading from '../../ui/Loading';
|
||||
import EmojiButton from './EmojiButton';
|
||||
@ -64,6 +65,8 @@ export type OwnProps = {
|
||||
addRecentCustomEmoji: ({ documentId }: { documentId: string }) => void;
|
||||
};
|
||||
|
||||
const INTERSECTION_THROTTLE = 200;
|
||||
|
||||
const EmojiTooltip: FC<OwnProps> = ({
|
||||
isOpen,
|
||||
emojis,
|
||||
@ -83,6 +86,10 @@ const EmojiTooltip: FC<OwnProps> = ({
|
||||
|
||||
useHorizontalScroll(containerRef);
|
||||
|
||||
const {
|
||||
observe: observeIntersection,
|
||||
} = useIntersectionObserver({ rootRef: containerRef, throttleMs: INTERSECTION_THROTTLE, isDisabled: !isOpen });
|
||||
|
||||
const handleSelectEmoji = useCallback((emoji: Emoji) => {
|
||||
onEmojiSelect(emoji.native);
|
||||
addRecentEmoji({ emoji: emoji.id });
|
||||
@ -148,6 +155,7 @@ const EmojiTooltip: FC<OwnProps> = ({
|
||||
emoji={emoji}
|
||||
focus={selectedIndex === index}
|
||||
onClick={handleCustomEmojiClick}
|
||||
observeIntersection={observeIntersection}
|
||||
/>
|
||||
)
|
||||
))
|
||||
|
||||
@ -27,6 +27,7 @@ interface Library {
|
||||
byKeyword: Record<string, Emoji[]>;
|
||||
names: string[];
|
||||
byName: Record<string, Emoji[]>;
|
||||
maxKeyLength: number;
|
||||
}
|
||||
|
||||
let emojiDataPromise: Promise<EmojiModule>;
|
||||
@ -34,6 +35,7 @@ let emojiRawData: EmojiRawData;
|
||||
let emojiData: EmojiData;
|
||||
|
||||
let RE_EMOJI_SEARCH: RegExp;
|
||||
let RE_LOWERCASE_TEST: RegExp;
|
||||
const EMOJIS_LIMIT = 36;
|
||||
const FILTER_MIN_LENGTH = 2;
|
||||
|
||||
@ -44,10 +46,12 @@ const prepareLibraryMemo = memoized(prepareLibrary);
|
||||
const searchInLibraryMemo = memoized(searchInLibrary);
|
||||
|
||||
try {
|
||||
RE_EMOJI_SEARCH = /(^|\s):[-+_:\p{L}\p{N}]*$/gui;
|
||||
RE_EMOJI_SEARCH = /(^|\s):(?!\s)[-+_:'\s\p{L}\p{N}]*$/gui;
|
||||
RE_LOWERCASE_TEST = /\p{Ll}/u;
|
||||
} catch (e) {
|
||||
// Support for older versions of firefox
|
||||
RE_EMOJI_SEARCH = /(^|\s):[-+_:\d\wа-яё]*$/gi;
|
||||
RE_EMOJI_SEARCH = /(^|\s):(?!\s)[-+_:'\s\d\wа-яёґєії]*$/gi;
|
||||
RE_LOWERCASE_TEST = /[a-zяёґєії]/;
|
||||
}
|
||||
|
||||
export default function useEmojiTooltip(
|
||||
@ -141,12 +145,13 @@ export default function useEmojiTooltip(
|
||||
|
||||
if (!filter) {
|
||||
matched = prepareRecentEmojisMemo(byId, recentEmojiIds, EMOJIS_LIMIT);
|
||||
} else if (filter.length >= FILTER_MIN_LENGTH) {
|
||||
} else if ((filter.length === 1 && RE_LOWERCASE_TEST.test(filter)) || filter.length >= FILTER_MIN_LENGTH) {
|
||||
const library = prepareLibraryMemo(byId, baseEmojiKeywords, emojiKeywords);
|
||||
matched = searchInLibraryMemo(library, filter, EMOJIS_LIMIT);
|
||||
matched = searchInLibraryMemo(library, filter.toLowerCase(), EMOJIS_LIMIT);
|
||||
}
|
||||
|
||||
if (!matched.length) {
|
||||
updateFiltered(MEMO_EMPTY_ARRAY);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -172,7 +177,7 @@ export default function useEmojiTooltip(
|
||||
|
||||
async function ensureEmojiData() {
|
||||
if (!emojiDataPromise) {
|
||||
emojiDataPromise = import('emoji-data-ios/emoji-data.json') as unknown as Promise<EmojiModule>;
|
||||
emojiDataPromise = import('emoji-data-ios/emoji-data.json');
|
||||
emojiRawData = (await emojiDataPromise).default;
|
||||
|
||||
emojiData = uncompressEmoji(emojiRawData);
|
||||
@ -224,21 +229,27 @@ function prepareLibrary(
|
||||
}, {} as Record<string, Emoji[]>);
|
||||
|
||||
const names = Object.keys(byName);
|
||||
const maxKeyLength = keywords.reduce((max, keyword) => Math.max(max, keyword.length), 0);
|
||||
|
||||
return {
|
||||
byKeyword,
|
||||
keywords,
|
||||
byName,
|
||||
names,
|
||||
maxKeyLength,
|
||||
};
|
||||
}
|
||||
|
||||
function searchInLibrary(library: Library, filter: string, limit: number) {
|
||||
const {
|
||||
byKeyword, keywords, byName, names,
|
||||
byKeyword, keywords, byName, names, maxKeyLength,
|
||||
} = library;
|
||||
|
||||
let matched: Emoji[] = MEMO_EMPTY_ARRAY;
|
||||
let matched: Emoji[] = [];
|
||||
|
||||
if (filter.length > maxKeyLength) {
|
||||
return MEMO_EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
const matchedKeywords = keywords.filter((keyword) => keyword.startsWith(filter)).sort();
|
||||
matched = matched.concat(Object.values(pickTruthy(byKeyword!, matchedKeywords)).flat());
|
||||
@ -249,5 +260,9 @@ function searchInLibrary(library: Library, filter: string, limit: number) {
|
||||
|
||||
matched = unique(matched);
|
||||
|
||||
if (!matched.length) {
|
||||
return MEMO_EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
return matched.slice(0, limit);
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ try {
|
||||
RE_USERNAME_SEARCH = /(^|\s)@[-_\p{L}\p{M}\p{N}]*$/gui;
|
||||
} catch (e) {
|
||||
// Support for older versions of Firefox
|
||||
RE_USERNAME_SEARCH = /(^|\s)@[-_\d\wа-яё]*$/gi;
|
||||
RE_USERNAME_SEARCH = /(^|\s)@[-_\d\wа-яёґєії]*$/gi;
|
||||
}
|
||||
|
||||
export default function useMentionTooltip(
|
||||
|
||||
@ -71,7 +71,7 @@ function scrollWithJs(container: HTMLElement, left: number, duration: number) {
|
||||
|
||||
if (t >= 1) {
|
||||
container.style.scrollSnapType = '';
|
||||
container.dataset.scrollId = undefined;
|
||||
delete container.dataset.scrollId;
|
||||
stopById.delete(id);
|
||||
resolve();
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ try {
|
||||
RE_NOT_LETTER = /[^\p{L}\p{M}]+/ui;
|
||||
} catch (e) {
|
||||
// Support for older versions of firefox
|
||||
RE_NOT_LETTER = /[^\wа-яё]+/i;
|
||||
RE_NOT_LETTER = /[^\wа-яёґєії]+/i;
|
||||
}
|
||||
|
||||
export default function searchWords(haystack: string, needle: string | string[]) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user