2019-05-16 22:32:11 +00:00
|
|
|
import * as React from 'react';
|
|
|
|
import classNames from 'classnames';
|
2019-12-17 18:52:36 +00:00
|
|
|
import { get, noop } from 'lodash';
|
2019-05-16 22:32:11 +00:00
|
|
|
import { Manager, Popper, Reference } from 'react-popper';
|
|
|
|
import { createPortal } from 'react-dom';
|
|
|
|
import { StickerPicker } from './StickerPicker';
|
2019-06-27 20:35:21 +00:00
|
|
|
import { countStickers } from './lib';
|
2019-05-16 22:32:11 +00:00
|
|
|
import { StickerPackType, StickerType } from '../../state/ducks/stickers';
|
|
|
|
import { LocalizerType } from '../../types/Util';
|
|
|
|
|
|
|
|
export type OwnProps = {
|
|
|
|
readonly i18n: LocalizerType;
|
|
|
|
readonly receivedPacks: ReadonlyArray<StickerPackType>;
|
|
|
|
readonly installedPacks: ReadonlyArray<StickerPackType>;
|
2019-05-24 01:27:42 +00:00
|
|
|
readonly blessedPacks: ReadonlyArray<StickerPackType>;
|
|
|
|
readonly knownPacks: ReadonlyArray<StickerPackType>;
|
2019-05-16 22:32:11 +00:00
|
|
|
readonly installedPack?: StickerPackType | null;
|
|
|
|
readonly recentStickers: ReadonlyArray<StickerType>;
|
|
|
|
readonly clearInstalledStickerPack: () => unknown;
|
|
|
|
readonly onClickAddPack: () => unknown;
|
|
|
|
readonly onPickSticker: (packId: string, stickerId: number) => unknown;
|
|
|
|
readonly showIntroduction?: boolean;
|
|
|
|
readonly clearShowIntroduction: () => unknown;
|
|
|
|
readonly showPickerHint: boolean;
|
|
|
|
readonly clearShowPickerHint: () => unknown;
|
2019-08-06 19:18:37 +00:00
|
|
|
readonly position?: 'top-end' | 'top-start';
|
2019-05-16 22:32:11 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export type Props = OwnProps;
|
|
|
|
|
|
|
|
export const StickerButton = React.memo(
|
|
|
|
({
|
|
|
|
i18n,
|
|
|
|
clearInstalledStickerPack,
|
|
|
|
onClickAddPack,
|
|
|
|
onPickSticker,
|
|
|
|
recentStickers,
|
|
|
|
receivedPacks,
|
|
|
|
installedPack,
|
|
|
|
installedPacks,
|
2019-05-24 01:27:42 +00:00
|
|
|
blessedPacks,
|
|
|
|
knownPacks,
|
2019-05-16 22:32:11 +00:00
|
|
|
showIntroduction,
|
|
|
|
clearShowIntroduction,
|
|
|
|
showPickerHint,
|
|
|
|
clearShowPickerHint,
|
2019-08-06 19:18:37 +00:00
|
|
|
position = 'top-end',
|
2019-05-16 22:32:11 +00:00
|
|
|
}: Props) => {
|
|
|
|
const [open, setOpen] = React.useState(false);
|
|
|
|
const [popperRoot, setPopperRoot] = React.useState<HTMLElement | null>(
|
|
|
|
null
|
|
|
|
);
|
|
|
|
|
2020-01-08 17:44:54 +00:00
|
|
|
const handleClickButton = React.useCallback(() => {
|
|
|
|
// Clear tooltip state
|
|
|
|
clearInstalledStickerPack();
|
|
|
|
clearShowIntroduction();
|
|
|
|
|
|
|
|
// Handle button click
|
|
|
|
if (installedPacks.length === 0) {
|
|
|
|
onClickAddPack();
|
|
|
|
} else if (popperRoot) {
|
|
|
|
setOpen(false);
|
|
|
|
} else {
|
|
|
|
setOpen(true);
|
|
|
|
}
|
|
|
|
}, [
|
|
|
|
clearInstalledStickerPack,
|
2020-09-14 22:14:03 +00:00
|
|
|
clearShowIntroduction,
|
2020-01-08 17:44:54 +00:00
|
|
|
installedPacks,
|
2020-09-14 22:14:03 +00:00
|
|
|
onClickAddPack,
|
2020-01-08 17:44:54 +00:00
|
|
|
popperRoot,
|
|
|
|
setOpen,
|
|
|
|
]);
|
2019-05-16 22:32:11 +00:00
|
|
|
|
|
|
|
const handlePickSticker = React.useCallback(
|
|
|
|
(packId: string, stickerId: number) => {
|
|
|
|
setOpen(false);
|
|
|
|
onPickSticker(packId, stickerId);
|
|
|
|
},
|
|
|
|
[setOpen, onPickSticker]
|
|
|
|
);
|
|
|
|
|
2020-01-08 17:44:54 +00:00
|
|
|
const handleClose = React.useCallback(() => {
|
|
|
|
setOpen(false);
|
|
|
|
}, [setOpen]);
|
2019-05-29 22:11:41 +00:00
|
|
|
|
2020-01-08 17:44:54 +00:00
|
|
|
const handleClickAddPack = React.useCallback(() => {
|
|
|
|
setOpen(false);
|
|
|
|
if (showPickerHint) {
|
|
|
|
clearShowPickerHint();
|
|
|
|
}
|
|
|
|
onClickAddPack();
|
|
|
|
}, [onClickAddPack, showPickerHint, clearShowPickerHint]);
|
2019-05-16 22:32:11 +00:00
|
|
|
|
2020-01-08 17:44:54 +00:00
|
|
|
const handleClearIntroduction = React.useCallback(() => {
|
|
|
|
clearInstalledStickerPack();
|
|
|
|
clearShowIntroduction();
|
|
|
|
}, [clearInstalledStickerPack, clearShowIntroduction]);
|
2019-05-16 22:32:11 +00:00
|
|
|
|
|
|
|
// Create popper root and handle outside clicks
|
2020-01-08 17:44:54 +00:00
|
|
|
React.useEffect(() => {
|
|
|
|
if (open) {
|
|
|
|
const root = document.createElement('div');
|
|
|
|
setPopperRoot(root);
|
|
|
|
document.body.appendChild(root);
|
|
|
|
const handleOutsideClick = ({ target }: MouseEvent) => {
|
|
|
|
const targetElement = target as HTMLElement;
|
|
|
|
const className = targetElement ? targetElement.className || '' : '';
|
|
|
|
|
|
|
|
// We need to special-case sticker picker header buttons, because they can
|
|
|
|
// disappear after being clicked, which breaks the .contains() check below.
|
|
|
|
const isMissingButtonClass =
|
|
|
|
!className ||
|
|
|
|
className.indexOf('module-sticker-picker__header__button') < 0;
|
|
|
|
|
|
|
|
if (!root.contains(targetElement) && isMissingButtonClass) {
|
|
|
|
setOpen(false);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
document.addEventListener('click', handleOutsideClick);
|
2019-05-16 22:32:11 +00:00
|
|
|
|
2020-01-08 17:44:54 +00:00
|
|
|
return () => {
|
|
|
|
document.body.removeChild(root);
|
|
|
|
document.removeEventListener('click', handleOutsideClick);
|
|
|
|
setPopperRoot(null);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return noop;
|
|
|
|
}, [open, setOpen, setPopperRoot]);
|
2019-05-16 22:32:11 +00:00
|
|
|
|
2019-11-07 21:36:16 +00:00
|
|
|
// Install keyboard shortcut to open sticker picker
|
2020-01-08 17:44:54 +00:00
|
|
|
React.useEffect(() => {
|
|
|
|
const handleKeydown = (event: KeyboardEvent) => {
|
|
|
|
const { ctrlKey, key, metaKey, shiftKey } = event;
|
|
|
|
const commandKey = get(window, 'platform') === 'darwin' && metaKey;
|
|
|
|
const controlKey = get(window, 'platform') !== 'darwin' && ctrlKey;
|
|
|
|
const commandOrCtrl = commandKey || controlKey;
|
|
|
|
|
|
|
|
// We don't want to open up if the conversation has any panels open
|
|
|
|
const panels = document.querySelectorAll('.conversation .panel');
|
|
|
|
if (panels && panels.length > 1) {
|
|
|
|
return;
|
|
|
|
}
|
2019-11-15 02:28:37 +00:00
|
|
|
|
2020-01-08 17:44:54 +00:00
|
|
|
if (commandOrCtrl && shiftKey && (key === 's' || key === 'S')) {
|
|
|
|
event.stopPropagation();
|
|
|
|
event.preventDefault();
|
2019-11-07 21:36:16 +00:00
|
|
|
|
2020-01-08 17:44:54 +00:00
|
|
|
setOpen(!open);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
document.addEventListener('keydown', handleKeydown);
|
2019-11-07 21:36:16 +00:00
|
|
|
|
2020-01-08 17:44:54 +00:00
|
|
|
return () => {
|
|
|
|
document.removeEventListener('keydown', handleKeydown);
|
|
|
|
};
|
|
|
|
}, [open, setOpen]);
|
2019-11-07 21:36:16 +00:00
|
|
|
|
2019-05-16 22:32:11 +00:00
|
|
|
// Clear the installed pack after one minute
|
2020-01-08 17:44:54 +00:00
|
|
|
React.useEffect(() => {
|
|
|
|
if (installedPack) {
|
|
|
|
const timerId = setTimeout(clearInstalledStickerPack, 10 * 1000);
|
2019-05-16 22:32:11 +00:00
|
|
|
|
2020-01-08 17:44:54 +00:00
|
|
|
return () => {
|
|
|
|
clearTimeout(timerId);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return noop;
|
|
|
|
}, [installedPack, clearInstalledStickerPack]);
|
2019-05-16 22:32:11 +00:00
|
|
|
|
2019-06-27 20:35:21 +00:00
|
|
|
if (
|
|
|
|
countStickers({
|
|
|
|
knownPacks,
|
|
|
|
blessedPacks,
|
|
|
|
installedPacks,
|
|
|
|
receivedPacks,
|
|
|
|
}) === 0
|
|
|
|
) {
|
2019-05-16 22:32:11 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Manager>
|
|
|
|
<Reference>
|
|
|
|
{({ ref }) => (
|
|
|
|
<button
|
2020-09-14 22:14:03 +00:00
|
|
|
type="button"
|
2019-05-16 22:32:11 +00:00
|
|
|
ref={ref}
|
|
|
|
onClick={handleClickButton}
|
|
|
|
className={classNames({
|
|
|
|
'module-sticker-button__button': true,
|
|
|
|
'module-sticker-button__button--active': open,
|
|
|
|
})}
|
2020-09-14 22:14:03 +00:00
|
|
|
aria-label={i18n('stickers--StickerPicker--Open')}
|
2019-05-16 22:32:11 +00:00
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</Reference>
|
|
|
|
{!open && !showIntroduction && installedPack ? (
|
2019-08-06 19:18:37 +00:00
|
|
|
<Popper placement={position} key={installedPack.id}>
|
2019-05-16 22:32:11 +00:00
|
|
|
{({ ref, style, placement, arrowProps }) => (
|
2019-11-07 21:36:16 +00:00
|
|
|
<button
|
2020-09-14 22:14:03 +00:00
|
|
|
type="button"
|
2019-05-16 22:32:11 +00:00
|
|
|
ref={ref}
|
|
|
|
style={style}
|
|
|
|
className="module-sticker-button__tooltip"
|
|
|
|
onClick={clearInstalledStickerPack}
|
|
|
|
>
|
2019-05-24 01:27:42 +00:00
|
|
|
{installedPack.cover ? (
|
|
|
|
<img
|
|
|
|
className="module-sticker-button__tooltip__image"
|
|
|
|
src={installedPack.cover.url}
|
|
|
|
alt={installedPack.title}
|
|
|
|
/>
|
|
|
|
) : (
|
|
|
|
<div className="module-sticker-button__tooltip__image-placeholder" />
|
|
|
|
)}
|
2019-05-16 22:32:11 +00:00
|
|
|
<span className="module-sticker-button__tooltip__text">
|
|
|
|
<span className="module-sticker-button__tooltip__text__title">
|
|
|
|
{installedPack.title}
|
|
|
|
</span>{' '}
|
|
|
|
installed
|
|
|
|
</span>
|
|
|
|
<div
|
|
|
|
ref={arrowProps.ref}
|
|
|
|
style={arrowProps.style}
|
|
|
|
className={classNames(
|
|
|
|
'module-sticker-button__tooltip__triangle',
|
|
|
|
`module-sticker-button__tooltip__triangle--${placement}`
|
|
|
|
)}
|
|
|
|
/>
|
2019-11-07 21:36:16 +00:00
|
|
|
</button>
|
2019-05-16 22:32:11 +00:00
|
|
|
)}
|
|
|
|
</Popper>
|
|
|
|
) : null}
|
|
|
|
{!open && showIntroduction ? (
|
2019-08-06 19:18:37 +00:00
|
|
|
<Popper placement={position}>
|
2019-05-16 22:32:11 +00:00
|
|
|
{({ ref, style, placement, arrowProps }) => (
|
2019-11-07 21:36:16 +00:00
|
|
|
<button
|
2020-09-14 22:14:03 +00:00
|
|
|
type="button"
|
2019-05-16 22:32:11 +00:00
|
|
|
ref={ref}
|
|
|
|
style={style}
|
|
|
|
className={classNames(
|
|
|
|
'module-sticker-button__tooltip',
|
|
|
|
'module-sticker-button__tooltip--introduction'
|
|
|
|
)}
|
|
|
|
onClick={handleClearIntroduction}
|
|
|
|
>
|
2019-12-12 03:10:37 +00:00
|
|
|
<img
|
|
|
|
className="module-sticker-button__tooltip--introduction__image"
|
|
|
|
srcSet="images/sticker_splash@1x.png 1x, images/sticker_splash@2x.png 2x"
|
|
|
|
alt={i18n('stickers--StickerManager--Introduction--Image')}
|
|
|
|
/>
|
2019-05-16 22:32:11 +00:00
|
|
|
<div className="module-sticker-button__tooltip--introduction__meta">
|
|
|
|
<div className="module-sticker-button__tooltip--introduction__meta__title">
|
|
|
|
{i18n('stickers--StickerManager--Introduction--Title')}
|
|
|
|
</div>
|
|
|
|
<div className="module-sticker-button__tooltip--introduction__meta__subtitle">
|
|
|
|
{i18n('stickers--StickerManager--Introduction--Body')}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="module-sticker-button__tooltip--introduction__close">
|
|
|
|
<button
|
2020-09-14 22:14:03 +00:00
|
|
|
type="button"
|
2019-05-16 22:32:11 +00:00
|
|
|
className="module-sticker-button__tooltip--introduction__close__button"
|
|
|
|
onClick={handleClearIntroduction}
|
2020-09-14 22:14:03 +00:00
|
|
|
aria-label={i18n('close')}
|
2019-05-16 22:32:11 +00:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
<div
|
|
|
|
ref={arrowProps.ref}
|
|
|
|
style={arrowProps.style}
|
|
|
|
className={classNames(
|
|
|
|
'module-sticker-button__tooltip__triangle',
|
|
|
|
'module-sticker-button__tooltip__triangle--introduction',
|
|
|
|
`module-sticker-button__tooltip__triangle--${placement}`
|
|
|
|
)}
|
|
|
|
/>
|
2019-11-07 21:36:16 +00:00
|
|
|
</button>
|
2019-05-16 22:32:11 +00:00
|
|
|
)}
|
|
|
|
</Popper>
|
|
|
|
) : null}
|
|
|
|
{open && popperRoot
|
|
|
|
? createPortal(
|
2019-08-06 19:18:37 +00:00
|
|
|
<Popper placement={position}>
|
2019-05-16 22:32:11 +00:00
|
|
|
{({ ref, style }) => (
|
|
|
|
<StickerPicker
|
|
|
|
ref={ref}
|
|
|
|
i18n={i18n}
|
|
|
|
style={style}
|
|
|
|
packs={installedPacks}
|
2019-05-29 22:11:41 +00:00
|
|
|
onClose={handleClose}
|
2019-05-16 22:32:11 +00:00
|
|
|
onClickAddPack={handleClickAddPack}
|
|
|
|
onPickSticker={handlePickSticker}
|
|
|
|
recentStickers={recentStickers}
|
|
|
|
showPickerHint={showPickerHint}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
</Popper>,
|
|
|
|
popperRoot
|
|
|
|
)
|
|
|
|
: null}
|
|
|
|
</Manager>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|