2022-08-25 11:35:19 +00:00
|
|
|
/* eslint-disable lines-around-comment */
|
|
|
|
import { GiphyFetch, TrendingOptions } from '@giphy/js-fetch-api';
|
2022-03-11 13:00:49 +00:00
|
|
|
import { Grid } from '@giphy/react-components';
|
2022-09-13 07:36:00 +00:00
|
|
|
import { Theme } from '@mui/material';
|
2022-03-11 13:00:49 +00:00
|
|
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
import { batch, useDispatch, useSelector } from 'react-redux';
|
2022-09-13 07:36:00 +00:00
|
|
|
import { makeStyles } from 'tss-react/mui';
|
2022-03-11 13:00:49 +00:00
|
|
|
|
2022-09-07 08:20:05 +00:00
|
|
|
import { createGifSentEvent } from '../../../analytics/AnalyticsEvents';
|
|
|
|
import { sendAnalytics } from '../../../analytics/functions';
|
2022-08-25 11:35:19 +00:00
|
|
|
import { IState } from '../../../app/types';
|
2022-09-16 10:05:15 +00:00
|
|
|
import Input from '../../../base/ui/components/web/Input';
|
2022-08-25 11:35:19 +00:00
|
|
|
// @ts-ignore
|
2022-03-11 13:00:49 +00:00
|
|
|
import { sendMessage } from '../../../chat/actions.any';
|
2022-08-25 11:35:19 +00:00
|
|
|
import { SCROLL_SIZE } from '../../../filmstrip/constants';
|
2022-03-11 13:00:49 +00:00
|
|
|
import { toggleReactionsMenuVisibility } from '../../../reactions/actions.web';
|
2022-08-25 11:35:19 +00:00
|
|
|
// @ts-ignore
|
2022-03-11 13:00:49 +00:00
|
|
|
import { setOverflowMenuVisible } from '../../../toolbox/actions.web';
|
2022-08-25 11:35:19 +00:00
|
|
|
// @ts-ignore
|
2022-03-11 13:00:49 +00:00
|
|
|
import { Drawer, JitsiPortal } from '../../../toolbox/components/web';
|
2022-08-25 11:35:19 +00:00
|
|
|
// @ts-ignore
|
2022-03-11 13:00:49 +00:00
|
|
|
import { showOverflowDrawer } from '../../../toolbox/functions.web';
|
2022-08-25 11:35:19 +00:00
|
|
|
// @ts-ignore
|
2022-03-11 13:00:49 +00:00
|
|
|
import { setGifDrawerVisibility } from '../../actions';
|
2022-08-25 11:35:19 +00:00
|
|
|
// @ts-ignore
|
2022-03-11 13:00:49 +00:00
|
|
|
import { formatGifUrlMessage, getGifAPIKey, getGifUrl } from '../../functions';
|
|
|
|
|
2022-09-13 07:36:00 +00:00
|
|
|
const OVERFLOW_DRAWER_PADDING = 16;
|
2022-03-11 13:00:49 +00:00
|
|
|
|
2022-09-13 07:36:00 +00:00
|
|
|
const useStyles = makeStyles()((theme: Theme) => {
|
2022-03-11 13:00:49 +00:00
|
|
|
return {
|
|
|
|
gifsMenu: {
|
|
|
|
width: '100%',
|
2022-09-13 07:36:00 +00:00
|
|
|
marginBottom: theme.spacing(2),
|
2022-03-11 13:00:49 +00:00
|
|
|
display: 'flex',
|
|
|
|
flexDirection: 'column',
|
|
|
|
|
|
|
|
'& div:focus': {
|
|
|
|
border: '1px solid red !important',
|
|
|
|
boxSizing: 'border-box'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
searchField: {
|
2022-09-13 07:36:00 +00:00
|
|
|
marginBottom: theme.spacing(3)
|
2022-03-11 13:00:49 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
gifContainer: {
|
|
|
|
height: '245px',
|
|
|
|
overflowY: 'auto'
|
|
|
|
},
|
|
|
|
|
|
|
|
logoContainer: {
|
|
|
|
width: `calc(100% - ${SCROLL_SIZE}px)`,
|
|
|
|
backgroundColor: '#121119',
|
|
|
|
display: 'flex',
|
|
|
|
alignItems: 'center',
|
|
|
|
justifyContent: 'center',
|
|
|
|
color: '#fff',
|
2022-09-13 07:36:00 +00:00
|
|
|
marginTop: theme.spacing(1)
|
2022-03-11 13:00:49 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
overflowMenu: {
|
2022-09-13 07:36:00 +00:00
|
|
|
padding: theme.spacing(3),
|
2022-03-11 13:00:49 +00:00
|
|
|
width: '100%',
|
|
|
|
boxSizing: 'border-box'
|
|
|
|
},
|
|
|
|
|
|
|
|
gifContainerOverflow: {
|
|
|
|
flexGrow: 1
|
|
|
|
},
|
|
|
|
|
|
|
|
drawer: {
|
|
|
|
display: 'flex',
|
|
|
|
height: '100%'
|
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gifs menu.
|
|
|
|
*
|
|
|
|
* @returns {ReactElement}
|
|
|
|
*/
|
|
|
|
function GifsMenu() {
|
2022-09-29 11:45:34 +00:00
|
|
|
const API_KEY = useSelector(getGifAPIKey);
|
2022-03-11 13:00:49 +00:00
|
|
|
const giphyFetch = new GiphyFetch(API_KEY);
|
2022-08-25 11:35:19 +00:00
|
|
|
const [ searchKey, setSearchKey ] = useState<string>();
|
2022-09-13 07:36:00 +00:00
|
|
|
const { classes: styles, cx } = useStyles();
|
2022-03-11 13:00:49 +00:00
|
|
|
const dispatch = useDispatch();
|
|
|
|
const { t } = useTranslation();
|
2022-08-25 11:35:19 +00:00
|
|
|
const overflowDrawer: boolean = useSelector(showOverflowDrawer);
|
|
|
|
const { clientWidth } = useSelector((state: IState) => state['features/base/responsive-ui']);
|
2022-03-11 13:00:49 +00:00
|
|
|
|
|
|
|
const fetchGifs = useCallback(async (offset = 0) => {
|
2022-08-25 11:35:19 +00:00
|
|
|
const options: TrendingOptions = {
|
2022-03-11 13:00:49 +00:00
|
|
|
rating: 'pg-13',
|
|
|
|
limit: 20,
|
|
|
|
offset
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!searchKey) {
|
|
|
|
return await giphyFetch.trending(options);
|
|
|
|
}
|
|
|
|
|
|
|
|
return await giphyFetch.search(searchKey, options);
|
|
|
|
}, [ searchKey ]);
|
|
|
|
|
|
|
|
const onDrawerClose = useCallback(() => {
|
|
|
|
dispatch(setGifDrawerVisibility(false));
|
|
|
|
dispatch(setOverflowMenuVisible(false));
|
2022-08-25 11:35:19 +00:00
|
|
|
}, []);
|
2022-03-11 13:00:49 +00:00
|
|
|
|
|
|
|
const handleGifClick = useCallback((gif, e) => {
|
|
|
|
e?.stopPropagation();
|
|
|
|
const url = getGifUrl(gif);
|
|
|
|
|
|
|
|
sendAnalytics(createGifSentEvent());
|
|
|
|
batch(() => {
|
|
|
|
dispatch(sendMessage(formatGifUrlMessage(url), true));
|
|
|
|
dispatch(toggleReactionsMenuVisibility());
|
|
|
|
overflowDrawer && onDrawerClose();
|
|
|
|
});
|
|
|
|
}, [ dispatch, overflowDrawer ]);
|
|
|
|
|
|
|
|
const handleGifKeyPress = useCallback((gif, e) => {
|
|
|
|
if (e.nativeEvent.keyCode === 13) {
|
|
|
|
handleGifClick(gif, null);
|
|
|
|
}
|
|
|
|
}, [ handleGifClick ]);
|
|
|
|
|
|
|
|
const handleSearchKeyChange = useCallback(value => {
|
|
|
|
setSearchKey(value);
|
2022-08-25 11:35:19 +00:00
|
|
|
}, []);
|
2022-03-11 13:00:49 +00:00
|
|
|
|
|
|
|
const handleKeyDown = useCallback(e => {
|
2022-08-25 11:35:19 +00:00
|
|
|
if (!document.activeElement) {
|
|
|
|
return;
|
|
|
|
}
|
2022-03-11 13:00:49 +00:00
|
|
|
if (e.keyCode === 38) { // up arrow
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
// if the first gif is focused move focus to the input
|
|
|
|
if (document.activeElement.previousElementSibling === null) {
|
2022-08-25 11:35:19 +00:00
|
|
|
const element = document.querySelector('.gif-input') as HTMLElement;
|
|
|
|
|
|
|
|
element?.focus();
|
2022-03-11 13:00:49 +00:00
|
|
|
} else {
|
2022-08-25 11:35:19 +00:00
|
|
|
const element = document.activeElement.previousElementSibling as HTMLElement;
|
|
|
|
|
|
|
|
element?.focus();
|
2022-03-11 13:00:49 +00:00
|
|
|
}
|
|
|
|
} else if (e.keyCode === 40) { // down arrow
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
// if the input is focused move focus to the first gif
|
|
|
|
if (document.activeElement.classList.contains('gif-input')) {
|
2022-08-25 11:35:19 +00:00
|
|
|
const element = document.querySelector('.giphy-gif') as HTMLElement;
|
|
|
|
|
|
|
|
element?.focus();
|
2022-03-11 13:00:49 +00:00
|
|
|
} else {
|
2022-08-25 11:35:19 +00:00
|
|
|
const element = document.activeElement.nextElementSibling as HTMLElement;
|
|
|
|
|
|
|
|
element?.focus();
|
2022-03-11 13:00:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
document.addEventListener('keydown', handleKeyDown);
|
|
|
|
|
|
|
|
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
// For some reason, the Grid component does not do an initial call on mobile.
|
|
|
|
// This fixes that.
|
|
|
|
useEffect(() => setSearchKey(''), []);
|
|
|
|
|
2022-09-22 08:34:46 +00:00
|
|
|
const onInputKeyPress = useCallback((e: React.KeyboardEvent) => {
|
|
|
|
e.stopPropagation();
|
|
|
|
}, []);
|
|
|
|
|
2022-03-11 13:00:49 +00:00
|
|
|
const gifMenu = (
|
|
|
|
<div
|
2022-09-13 07:36:00 +00:00
|
|
|
className = { cx(styles.gifsMenu,
|
2022-03-11 13:00:49 +00:00
|
|
|
overflowDrawer && styles.overflowMenu
|
|
|
|
) }>
|
2022-09-16 10:05:15 +00:00
|
|
|
<Input
|
2022-03-11 13:00:49 +00:00
|
|
|
autoFocus = { true }
|
2022-09-13 07:36:00 +00:00
|
|
|
className = { cx(styles.searchField, 'gif-input') }
|
2022-03-11 13:00:49 +00:00
|
|
|
onChange = { handleSearchKeyChange }
|
2022-09-22 08:34:46 +00:00
|
|
|
onKeyPress = { onInputKeyPress }
|
2022-09-16 10:05:15 +00:00
|
|
|
placeholder = { t('giphy.search') }
|
|
|
|
// eslint-disable-next-line react/jsx-no-bind
|
|
|
|
ref = { inputElement => {
|
|
|
|
inputElement?.focus();
|
|
|
|
setTimeout(() => inputElement?.focus(), 200);
|
|
|
|
} }
|
|
|
|
type = 'text'
|
|
|
|
value = { searchKey ?? '' } />
|
2022-03-11 13:00:49 +00:00
|
|
|
<div
|
2022-09-13 07:36:00 +00:00
|
|
|
className = { cx(styles.gifContainer,
|
2022-03-11 13:00:49 +00:00
|
|
|
overflowDrawer && styles.gifContainerOverflow) }>
|
|
|
|
<Grid
|
|
|
|
columns = { 2 }
|
|
|
|
fetchGifs = { fetchGifs }
|
|
|
|
gutter = { 6 }
|
|
|
|
hideAttribution = { true }
|
|
|
|
key = { searchKey }
|
|
|
|
noLink = { true }
|
|
|
|
noResultsMessage = { t('giphy.noResults') }
|
|
|
|
onGifClick = { handleGifClick }
|
|
|
|
onGifKeyPress = { handleGifKeyPress }
|
|
|
|
width = { overflowDrawer
|
|
|
|
? clientWidth - (2 * OVERFLOW_DRAWER_PADDING) - SCROLL_SIZE
|
|
|
|
: 320
|
|
|
|
} />
|
|
|
|
</div>
|
|
|
|
<div className = { styles.logoContainer }>
|
|
|
|
<span>Powered by</span>
|
|
|
|
<img
|
|
|
|
alt = 'GIPHY Logo'
|
|
|
|
src = 'images/GIPHY_logo.png' />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
|
|
|
|
return overflowDrawer ? (
|
|
|
|
<JitsiPortal>
|
|
|
|
<Drawer
|
|
|
|
className = { styles.drawer }
|
|
|
|
isOpen = { true }
|
|
|
|
onClose = { onDrawerClose }>
|
|
|
|
{gifMenu}
|
|
|
|
</Drawer>
|
|
|
|
</JitsiPortal>
|
|
|
|
) : gifMenu;
|
|
|
|
}
|
|
|
|
|
|
|
|
export default GifsMenu;
|