bigbluebutton-Github/bigbluebutton-html5/imports/ui/components/audio/audio-graphql/audio-controls/component.tsx

176 lines
5.5 KiB
TypeScript
Raw Normal View History

2023-09-13 22:31:20 +08:00
/* eslint-disable no-underscore-dangle */
/* eslint-disable @typescript-eslint/ban-ts-comment */
2023-07-25 02:56:40 +08:00
import React, { useCallback, useEffect, useMemo } from 'react';
import { User } from '/imports/ui/Types/user';
2023-09-14 01:57:43 +08:00
import useCurrentUser from '/imports/ui/core/hooks/useCurrentUser';
import useMeeting from '/imports/ui/core/hooks/useMeeting';
2023-07-25 02:56:40 +08:00
import { Meeting } from '/imports/ui/Types/meeting';
import { useShortcut } from '/imports/ui/core/hooks/useShortcut';
2023-07-25 02:56:40 +08:00
import { useMutation, useReactiveVar } from '@apollo/client';
import { defineMessages, useIntl } from 'react-intl';
import Button from '/imports/ui/components/common/button/component';
import AudioModalContainer from '../../audio-modal/container';
import AudioManager from '/imports/ui/services/audio-manager';
import { joinListenOnly } from './service';
import Styled from './styles';
import InputStreamLiveSelectorContainer from './input-stream-live-selector/component';
import { getSettingsSingletonInstance } from '/imports/ui/services/settings';
2023-07-25 02:56:40 +08:00
import { UPDATE_ECHO_TEST_RUNNING } from './queries';
const intlMessages = defineMessages({
joinAudio: {
id: 'app.audio.joinAudio',
description: 'Join audio button label',
},
joinAudioAndSetActive: {
id: 'app.audio.joinAudioAndSetActive',
description: 'Join audio button label when user is away',
},
2023-07-25 02:56:40 +08:00
leaveAudio: {
id: 'app.audio.leaveAudio',
description: 'Leave audio button label',
},
muteAudio: {
id: 'app.actionsBar.muteLabel',
description: 'Mute audio button label',
},
unmuteAudio: {
id: 'app.actionsBar.unmuteLabel',
description: 'Unmute audio button label',
},
});
interface AudioControlsProps {
inAudio: boolean;
isConnected: boolean;
disabled: boolean;
isEchoTest: boolean;
updateEchoTestRunning: () => void;
away: boolean;
isConnecting?: boolean;
2023-09-13 22:31:20 +08:00
}
2023-07-25 02:56:40 +08:00
const AudioControls: React.FC<AudioControlsProps> = ({
isConnected,
disabled,
inAudio,
isEchoTest,
updateEchoTestRunning,
away,
isConnecting,
2023-07-25 02:56:40 +08:00
}) => {
const intl = useIntl();
const joinAudioShortcut = useShortcut('joinAudio');
2023-09-11 21:12:37 +08:00
const echoTestIntervalRef = React.useRef<ReturnType<typeof setTimeout>>();
const Settings = getSettingsSingletonInstance();
const animations = Settings?.application?.animations;
2023-07-25 02:56:40 +08:00
const [isAudioModalOpen, setIsAudioModalOpen] = React.useState(false);
2023-09-11 21:12:37 +08:00
const handleJoinAudio = useCallback((connected: boolean) => {
if (connected) {
2023-07-25 02:56:40 +08:00
joinListenOnly();
} else {
setIsAudioModalOpen(true);
}
2023-09-11 21:12:37 +08:00
}, []);
2023-07-25 02:56:40 +08:00
const joinButton = useMemo(() => {
const joinAudioLabel = away ? intlMessages.joinAudioAndSetActive : intlMessages.joinAudio;
2023-07-25 02:56:40 +08:00
return (
2023-09-13 22:31:20 +08:00
// eslint-disable-next-line jsx-a11y/no-access-key
2023-07-25 02:56:40 +08:00
<Button
onClick={() => handleJoinAudio(isConnected)}
disabled={disabled}
hideLabel
aria-label={intl.formatMessage(joinAudioLabel)}
label={intl.formatMessage(joinAudioLabel)}
2023-07-25 02:56:40 +08:00
data-test="joinAudio"
color="default"
ghost
icon="no_audio"
size="lg"
circle
accessKey={joinAudioShortcut}
2023-07-25 02:56:40 +08:00
/>
2023-09-13 22:31:20 +08:00
);
}, [isConnected, disabled, joinAudioShortcut, away]);
2023-07-25 02:56:40 +08:00
useEffect(() => {
if (isEchoTest) {
echoTestIntervalRef.current = setInterval(() => {
updateEchoTestRunning();
}, 1000);
} else {
clearInterval(echoTestIntervalRef.current);
}
}, [isEchoTest]);
return (
<Styled.Container>
{isConnecting ? (
<Styled.SpinnerOverlay animations={animations}>
<Styled.Bounce1 animations={animations} />
<Styled.Bounce2 animations={animations} />
</Styled.SpinnerOverlay>
) : (
<>
{!inAudio ? joinButton : <InputStreamLiveSelectorContainer />}
{isAudioModalOpen && (
<AudioModalContainer
priority="low"
setIsOpen={() => setIsAudioModalOpen(false)}
isOpen={isAudioModalOpen}
/>
)}
</>
)}
2023-07-25 02:56:40 +08:00
</Styled.Container>
);
};
export const AudioControlsContainer: React.FC = () => {
const { data: currentUser } = useCurrentUser((u: Partial<User>) => ({
presenter: u.presenter,
isModerator: u.isModerator,
locked: u?.locked ?? false,
voice: u.voice,
away: u.away,
}));
const { data: currentMeeting } = useMeeting((m: Partial<Meeting>) => ({
lockSettings: m.lockSettings,
}));
2023-07-25 02:56:40 +08:00
const [updateEchoTestRunning] = useMutation(UPDATE_ECHO_TEST_RUNNING);
2023-09-13 22:31:20 +08:00
// I access the internal variable to get the makevar reference,
// so we doesn't broke the client that uses the value directly
2023-07-25 02:56:40 +08:00
// and I can use it to make my component reactive
// @ts-ignore - temporary while hybrid (meteor+GraphQl)
2023-07-25 02:56:40 +08:00
const isConnected = useReactiveVar(AudioManager._isConnected.value) as boolean;
// @ts-ignore - temporary while hybrid (meteor+GraphQl)
2023-07-25 02:56:40 +08:00
const isConnecting = useReactiveVar(AudioManager._isConnecting.value) as boolean;
// @ts-ignore - temporary while hybrid (meteor+GraphQl)
2023-07-25 02:56:40 +08:00
const isHangingUp = useReactiveVar(AudioManager._isHangingUp.value) as boolean;
// @ts-ignore - temporary while hybrid (meteor+GraphQl)
2023-07-25 02:56:40 +08:00
const isEchoTest = useReactiveVar(AudioManager._isEchoTest.value) as boolean;
if (!currentUser || !currentMeeting) return null;
2023-09-13 22:31:20 +08:00
return (
<AudioControls
inAudio={!!currentUser.voice ?? false}
2023-09-13 22:31:20 +08:00
isConnected={isConnected}
disabled={isConnecting || isHangingUp}
isEchoTest={isEchoTest}
updateEchoTestRunning={updateEchoTestRunning}
away={currentUser.away || false}
isConnecting={isConnecting}
2023-09-13 22:31:20 +08:00
/>
);
2023-07-25 02:56:40 +08:00
};
export default AudioControlsContainer;