Files
upstream-jitsi-meet/react/features/filmstrip/components/web/TranslationPendingChip.tsx
upstream-mirror 1d2bfcac0c
Some checks failed
Prosody Plugin Tests / test (push) Failing after 10m11s
Close stale issues and PRs / stale (push) Failing after 1s
snapshot of https://github.com/jitsi/jitsi-meet.git @ fd1749dd28f4138aa80234fc7f311b19aaffa0e7 (2026-09-15)
2026-09-15 15:56:32 +05:00

114 lines
3.1 KiB
TypeScript

import React from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { keyframes } from 'tss-react';
import { makeStyles } from 'tss-react/mui';
import { IReduxState } from '../../../app/types';
import { getTranslationDeliveryPendingCount } from '../../../audio-translation/functions';
import { THUMBNAIL_TYPE } from '../../constants';
/**
* Amber, deliberately not the dominant-speaker blue.
*/
const PENDING_COLOR = '#F8AE1A';
interface IProps {
/**
* The speaker whose translated audio is still being delivered.
*/
participantId: string;
/**
* The type of thumbnail; drives the compact (count-only) form.
*/
thumbnailType?: string;
}
const useStyles = makeStyles()(() => {
const breathe = keyframes`
0% { opacity: 0.45; }
50% { opacity: 1; }
100% { opacity: 0.45; }
`;
return {
chip: {
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.72)',
border: `1px solid ${PENDING_COLOR}`,
borderRadius: '12px',
color: '#FFD98A',
display: 'flex',
gap: '4px',
padding: '2px 6px',
position: 'absolute' as const,
right: '4px',
top: '4px',
whiteSpace: 'nowrap' as const,
zIndex: 12
},
compact: {
fontSize: '9px',
lineHeight: '12px'
},
dot: {
animation: `${breathe} 1.6s ease-in-out infinite`,
backgroundColor: PENDING_COLOR,
borderRadius: '50%',
flexShrink: 0,
height: '8px',
width: '8px',
'@media (prefers-reduced-motion: reduce)': {
animation: 'none',
opacity: 0.85
}
},
label: {
fontSize: '10px',
lineHeight: '13px'
}
};
});
/**
* Corner chip counting the participants still hearing this speaker's translated audio. Renders nothing when
* no count was published (the ring alone still signals the wait).
*
* @param {IProps} props - The component's props.
* @returns {ReactElement|null}
*/
const TranslationPendingChip = ({ participantId, thumbnailType }: IProps) => {
const { classes, cx } = useStyles();
const { t } = useTranslation();
const count = useSelector((state: IReduxState) =>
getTranslationDeliveryPendingCount(state, participantId));
if (count < 1) {
return null;
}
const isCompact = thumbnailType !== THUMBNAIL_TYPE.TILE;
const tooltip = t('videothumbnail.translationStillListening', { count });
const label = t('videothumbnail.translationStillListeningShort', { num: count });
return (
<div
aria-label = { tooltip }
className = { cx(classes.chip, isCompact && classes.compact) }
title = { tooltip }>
<span className = { classes.dot } />
<span className = { classes.label }>
{ isCompact ? count : label }
</span>
</div>
);
};
export default TranslationPendingChip;