Emoji Tooltip: Update recent emojis (#1066)

This commit is contained in:
Alexander Zinchuk 2021-05-11 02:57:05 +03:00
parent 0cf1bbb72c
commit 1436cb6908
3 changed files with 28 additions and 15 deletions

View File

@ -811,6 +811,7 @@ const Composer: FC<OwnProps & StateProps & DispatchProps> = ({
emojis={filteredEmojis} emojis={filteredEmojis}
onClose={closeEmojiTooltip} onClose={closeEmojiTooltip}
onEmojiSelect={insertEmoji} onEmojiSelect={insertEmoji}
addRecentEmoji={addRecentEmoji}
/> />
<AttachMenu <AttachMenu
isOpen={isAttachMenuOpen} isOpen={isAttachMenuOpen}

View File

@ -10,6 +10,7 @@ import findInViewport from '../../../util/findInViewport';
import isFullyVisible from '../../../util/isFullyVisible'; import isFullyVisible from '../../../util/isFullyVisible';
import fastSmoothScrollHorizontal from '../../../util/fastSmoothScrollHorizontal'; import fastSmoothScrollHorizontal from '../../../util/fastSmoothScrollHorizontal';
import useShowTransition from '../../../hooks/useShowTransition'; import useShowTransition from '../../../hooks/useShowTransition';
import usePrevDuringAnimation from '../../../hooks/usePrevDuringAnimation';
import Loading from '../../ui/Loading'; import Loading from '../../ui/Loading';
import EmojiButton from './EmojiButton'; import EmojiButton from './EmojiButton';
@ -51,18 +52,23 @@ export type OwnProps = {
isOpen: boolean; isOpen: boolean;
onEmojiSelect: (text: string) => void; onEmojiSelect: (text: string) => void;
onClose: NoneToVoidFunction; onClose: NoneToVoidFunction;
addRecentEmoji: AnyToVoidFunction;
emojis: Emoji[]; emojis: Emoji[];
}; };
const CLOSE_DURATION = 350;
const EmojiTooltip: FC<OwnProps> = ({ const EmojiTooltip: FC<OwnProps> = ({
isOpen, isOpen,
emojis, emojis,
onClose, onClose,
onEmojiSelect, onEmojiSelect,
addRecentEmoji,
}) => { }) => {
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const { shouldRender, transitionClassNames } = useShowTransition(isOpen, undefined, undefined, false); 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(-1);
@ -95,9 +101,10 @@ const EmojiTooltip: FC<OwnProps> = ({
if (emoji) { if (emoji) {
e.preventDefault(); e.preventDefault();
onEmojiSelect(emoji.native); onEmojiSelect(emoji.native);
addRecentEmoji({ emoji: emoji.id });
} }
} }
}, [emojis, onEmojiSelect, selectedIndex]); }, [addRecentEmoji, emojis, onEmojiSelect, selectedIndex]);
useEffect(() => (isOpen ? captureKeyboardListeners({ useEffect(() => (isOpen ? captureKeyboardListeners({
onEsc: onClose, onEsc: onClose,
@ -126,8 +133,8 @@ const EmojiTooltip: FC<OwnProps> = ({
onMouseEnter={!IS_TOUCH_ENV ? handleMouseEnter : undefined} onMouseEnter={!IS_TOUCH_ENV ? handleMouseEnter : undefined}
onMouseLeave={!IS_TOUCH_ENV ? handleMouseLeave : undefined} onMouseLeave={!IS_TOUCH_ENV ? handleMouseLeave : undefined}
> >
{shouldRender && emojis ? ( {shouldRender && listEmojis ? (
emojis.map((emoji, index) => ( listEmojis.map((emoji, index) => (
<EmojiButton <EmojiButton
key={emoji.id} key={emoji.id}
emoji={emoji} emoji={emoji}

View File

@ -15,7 +15,7 @@ let emojiRawData: EmojiRawData;
let emojiData: EmojiData; let emojiData: EmojiData;
const RE_NOT_EMOJI_SEARCH = /[^-:_a-z\d]+/i; const RE_NOT_EMOJI_SEARCH = /[^-:_a-z\d]+/i;
const EMOJIS_LIMIT = 50; const EMOJIS_LIMIT = 36;
export default function useEmojiTooltip( export default function useEmojiTooltip(
isAllowed: boolean, isAllowed: boolean,
@ -25,24 +25,26 @@ export default function useEmojiTooltip(
onUpdateHtml: (html: string) => void, onUpdateHtml: (html: string) => void,
) { ) {
const [isOpen, markIsOpen, unmarkIsOpen] = useFlag(); const [isOpen, markIsOpen, unmarkIsOpen] = useFlag();
const [emojis, setEmojis] = useState<Emoji[]>([]); const [emojiIds, setEmojiIds] = useState<string[]>([]);
const [filteredEmojis, setFilteredEmojis] = useState<Emoji[]>([]); const [filteredEmojis, setFilteredEmojis] = useState<Emoji[]>([]);
const recentEmojis = useMemo( const recentEmojis = useMemo(
() => { () => {
if (!emojis && !recentEmojiIds.length) { if (!emojiIds.length || !recentEmojiIds.length) {
return []; return [];
} }
return emojis.filter((emoji) => recentEmojiIds.includes(emoji.id)) as Emoji[]; return recentEmojiIds
.map((emojiId) => emojiData.emojis[emojiId])
.filter<Emoji>(Boolean as any);
}, },
[emojis, recentEmojiIds], [emojiIds, recentEmojiIds],
); );
// Initialize data on first render. // Initialize data on first render.
useEffect(() => { useEffect(() => {
const exec = () => { const exec = () => {
setEmojis(Object.values(emojiData.emojis)); setEmojiIds(Object.keys(emojiData.emojis));
}; };
if (emojiData) { if (emojiData) {
@ -54,7 +56,7 @@ export default function useEmojiTooltip(
}, []); }, []);
useEffect(() => { useEffect(() => {
if (!html || !emojis) { if (!html || !emojiIds.length) {
unmarkIsOpen(); unmarkIsOpen();
return; return;
} }
@ -67,17 +69,20 @@ export default function useEmojiTooltip(
} }
const filter = code.substr(1); const filter = code.substr(1);
const matched = filter === '' ? recentEmojis : emojis.filter((emoji) => { const matched = filter === ''
return 'names' in emoji && (!filter || emoji.names.find((name) => name.includes(filter))); ? recentEmojis
}) as Emoji[]; : emojiIds
.filter((emojiId) => emojiData.emojis[emojiId].names.find((name) => name.includes(filter)))
.slice(0, EMOJIS_LIMIT)
.map((emojiId) => emojiData.emojis[emojiId]);
if (matched.length) { if (matched.length) {
markIsOpen(); markIsOpen();
setFilteredEmojis(matched.slice(0, EMOJIS_LIMIT)); setFilteredEmojis(matched);
} else { } else {
unmarkIsOpen(); unmarkIsOpen();
} }
}, [emojis, html, markIsOpen, recentEmojis, unmarkIsOpen]); }, [emojiIds, html, markIsOpen, recentEmojis, unmarkIsOpen]);
const insertEmoji = useCallback((textEmoji: string) => { const insertEmoji = useCallback((textEmoji: string) => {
const atIndex = html.lastIndexOf(':'); const atIndex = html.lastIndexOf(':');