mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-05-19 02:07:48 +00:00
Currently the clientWidth is not representing the window width but it is representing the available video space width since we are subtracting the width of the participants pane and chat area.
254 lines
8.2 KiB
TypeScript
254 lines
8.2 KiB
TypeScript
import { GiphyFetch, TrendingOptions } from '@giphy/js-fetch-api';
|
|
import { Grid } from '@giphy/react-components';
|
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { batch, useDispatch, useSelector } from 'react-redux';
|
|
import { makeStyles } from 'tss-react/mui';
|
|
|
|
import { createGifSentEvent } from '../../../analytics/AnalyticsEvents';
|
|
import { sendAnalytics } from '../../../analytics/functions';
|
|
import { IReduxState } from '../../../app/types';
|
|
import Input from '../../../base/ui/components/web/Input';
|
|
import { sendMessage } from '../../../chat/actions.any';
|
|
import { SCROLL_SIZE } from '../../../filmstrip/constants';
|
|
import { toggleReactionsMenuVisibility } from '../../../reactions/actions.web';
|
|
import { IReactionsMenuParent } from '../../../reactions/types';
|
|
import Drawer from '../../../toolbox/components/web/Drawer';
|
|
import JitsiPortal from '../../../toolbox/components/web/JitsiPortal';
|
|
import { setGifMenuVisibility } from '../../actions';
|
|
import {
|
|
formatGifUrlMessage,
|
|
getGifAPIKey,
|
|
getGifRating,
|
|
getGifUrl
|
|
} from '../../function.any';
|
|
|
|
const OVERFLOW_DRAWER_PADDING = 16;
|
|
|
|
const useStyles = makeStyles()(theme => {
|
|
return {
|
|
gifsMenu: {
|
|
width: '100%',
|
|
marginBottom: theme.spacing(2),
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
|
|
'& div:focus': {
|
|
border: '1px solid red !important',
|
|
boxSizing: 'border-box'
|
|
}
|
|
},
|
|
|
|
searchField: {
|
|
marginBottom: theme.spacing(3)
|
|
},
|
|
|
|
gifContainer: {
|
|
height: '245px',
|
|
overflowY: 'auto'
|
|
},
|
|
|
|
logoContainer: {
|
|
width: `calc(100% - ${SCROLL_SIZE}px)`,
|
|
backgroundColor: '#121119',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
color: '#fff',
|
|
marginTop: theme.spacing(1)
|
|
},
|
|
|
|
overflowDrawerMenu: {
|
|
padding: theme.spacing(3),
|
|
width: '100%',
|
|
boxSizing: 'border-box',
|
|
height: '100%'
|
|
},
|
|
|
|
overflowMenu: {
|
|
height: '200px',
|
|
width: '201px',
|
|
marginBottom: '0px'
|
|
},
|
|
|
|
gifContainerOverflow: {
|
|
flexGrow: 1
|
|
},
|
|
|
|
drawer: {
|
|
display: 'flex',
|
|
height: '100%'
|
|
}
|
|
};
|
|
});
|
|
|
|
interface IProps {
|
|
columns?: number;
|
|
parent: IReactionsMenuParent;
|
|
}
|
|
|
|
/**
|
|
* Gifs menu.
|
|
*
|
|
* @returns {ReactElement}
|
|
*/
|
|
function GifsMenu({ columns = 2, parent }: IProps) {
|
|
const API_KEY = useSelector(getGifAPIKey);
|
|
const giphyFetch = new GiphyFetch(API_KEY);
|
|
const [ searchKey, setSearchKey ] = useState<string>();
|
|
const { classes: styles, cx } = useStyles();
|
|
const dispatch = useDispatch();
|
|
const { t } = useTranslation();
|
|
const isInOverflowMenu
|
|
= parent === IReactionsMenuParent.OverflowDrawer || parent === IReactionsMenuParent.OverflowMenu;
|
|
const { videoSpaceWidth } = useSelector((state: IReduxState) => state['features/base/responsive-ui']);
|
|
const rating = useSelector(getGifRating);
|
|
|
|
const fetchGifs = useCallback(async (offset = 0) => {
|
|
const options: TrendingOptions = {
|
|
limit: 20,
|
|
offset,
|
|
rating
|
|
};
|
|
|
|
if (!searchKey) {
|
|
return await giphyFetch.trending(options);
|
|
}
|
|
|
|
return await giphyFetch.search(searchKey, options);
|
|
}, [ searchKey ]);
|
|
|
|
const onDrawerClose = useCallback(() => {
|
|
dispatch(setGifMenuVisibility(false));
|
|
}, []);
|
|
|
|
const handleGifClick = useCallback((gif, e) => {
|
|
e?.stopPropagation();
|
|
const url = getGifUrl(gif);
|
|
|
|
sendAnalytics(createGifSentEvent());
|
|
batch(() => {
|
|
dispatch(sendMessage(formatGifUrlMessage(url), true));
|
|
dispatch(toggleReactionsMenuVisibility());
|
|
isInOverflowMenu && onDrawerClose();
|
|
});
|
|
}, [ dispatch, isInOverflowMenu ]);
|
|
|
|
const handleGifKeyPress = useCallback((gif, e) => {
|
|
if (e.nativeEvent.keyCode === 13) {
|
|
handleGifClick(gif, null);
|
|
}
|
|
}, [ handleGifClick ]);
|
|
|
|
const handleSearchKeyChange = useCallback(value => {
|
|
setSearchKey(value);
|
|
}, []);
|
|
|
|
const handleKeyDown = useCallback(e => {
|
|
if (!document.activeElement) {
|
|
return;
|
|
}
|
|
if (e.keyCode === 38) { // up arrow
|
|
e.preventDefault();
|
|
|
|
// if the first gif is focused move focus to the input
|
|
if (document.activeElement.previousElementSibling === null) {
|
|
const element = document.querySelector('.gif-input') as HTMLElement;
|
|
|
|
element?.focus();
|
|
} else {
|
|
const element = document.activeElement.previousElementSibling as HTMLElement;
|
|
|
|
element?.focus();
|
|
}
|
|
} 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')) {
|
|
const element = document.querySelector('.giphy-gif') as HTMLElement;
|
|
|
|
element?.focus();
|
|
} else {
|
|
const element = document.activeElement.nextElementSibling as HTMLElement;
|
|
|
|
element?.focus();
|
|
}
|
|
}
|
|
}, []);
|
|
|
|
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(''), []);
|
|
|
|
const onInputKeyPress = useCallback((e: React.KeyboardEvent) => {
|
|
e.stopPropagation();
|
|
}, []);
|
|
|
|
const gifMenu = (
|
|
<div
|
|
className = { cx(styles.gifsMenu,
|
|
parent === IReactionsMenuParent.OverflowDrawer && styles.overflowDrawerMenu,
|
|
parent === IReactionsMenuParent.OverflowMenu && styles.overflowMenu
|
|
) }>
|
|
<Input
|
|
autoFocus = { true }
|
|
className = { cx(styles.searchField, 'gif-input') }
|
|
id = 'gif-search-input'
|
|
onChange = { handleSearchKeyChange }
|
|
onKeyPress = { onInputKeyPress }
|
|
placeholder = { t('giphy.search') }
|
|
// eslint-disable-next-line react/jsx-no-bind
|
|
ref = { inputElement => {
|
|
inputElement?.focus();
|
|
setTimeout(() => inputElement?.focus(), 200);
|
|
} }
|
|
type = 'text'
|
|
value = { searchKey ?? '' } />
|
|
<div
|
|
className = { cx(styles.gifContainer,
|
|
parent === IReactionsMenuParent.OverflowDrawer && styles.gifContainerOverflow) }>
|
|
<Grid
|
|
columns = { columns }
|
|
fetchGifs = { fetchGifs }
|
|
gutter = { 6 }
|
|
hideAttribution = { true }
|
|
key = { searchKey }
|
|
noLink = { true }
|
|
noResultsMessage = { t('giphy.noResults') }
|
|
onGifClick = { handleGifClick }
|
|
onGifKeyPress = { handleGifKeyPress }
|
|
width = { parent === IReactionsMenuParent.OverflowDrawer
|
|
? videoSpaceWidth - (2 * OVERFLOW_DRAWER_PADDING) - SCROLL_SIZE
|
|
: parent === IReactionsMenuParent.OverflowMenu ? 201 : 320
|
|
} />
|
|
</div>
|
|
<div className = { styles.logoContainer }>
|
|
<span>Powered by</span>
|
|
<img
|
|
alt = 'GIPHY Logo'
|
|
src = 'images/GIPHY_logo.png' />
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
return parent === IReactionsMenuParent.OverflowDrawer ? (
|
|
<JitsiPortal>
|
|
<Drawer
|
|
className = { styles.drawer }
|
|
isOpen = { true }
|
|
onClose = { onDrawerClose }>
|
|
{gifMenu}
|
|
</Drawer>
|
|
</JitsiPortal>
|
|
) : gifMenu;
|
|
}
|
|
|
|
export default GifsMenu;
|