2024-06-04 21:40:54 +08:00
|
|
|
import { useMutation } from '@apollo/client';
|
2023-12-12 00:21:00 +08:00
|
|
|
import React, { useEffect, useMemo } from 'react';
|
|
|
|
import { defineMessages, useIntl } from 'react-intl';
|
|
|
|
import Styled from './styles';
|
|
|
|
import ModalFullscreen from '/imports/ui/components/common/modal/fullscreen/component';
|
|
|
|
import {
|
|
|
|
BreakoutRoom,
|
|
|
|
getBreakoutCount,
|
|
|
|
GetBreakoutCountResponse,
|
|
|
|
getBreakoutData,
|
|
|
|
GetBreakoutDataResponse,
|
2024-07-05 04:00:06 +08:00
|
|
|
handleInviteDismissedAt,
|
2023-12-12 00:21:00 +08:00
|
|
|
} from './queries';
|
|
|
|
import useCurrentUser from '/imports/ui/core/hooks/useCurrentUser';
|
2024-04-04 08:54:32 +08:00
|
|
|
import { BREAKOUT_ROOM_REQUEST_JOIN_URL } from '../../breakout-room/mutations';
|
2024-06-04 21:40:54 +08:00
|
|
|
import useDeduplicatedSubscription from '/imports/ui/core/hooks/useDeduplicatedSubscription';
|
2024-05-21 23:31:17 +08:00
|
|
|
import AudioManager from '/imports/ui/services/audio-manager';
|
|
|
|
import AudioService from '/imports/ui/components/audio/service';
|
2024-06-17 19:54:03 +08:00
|
|
|
import VideoService from '/imports/ui/components/video-provider/service';
|
|
|
|
import { useExitVideo, useStreams } from '/imports/ui/components/video-provider/hooks';
|
2024-05-21 23:31:17 +08:00
|
|
|
import logger from '/imports/startup/client/logger';
|
|
|
|
import { rejoinAudio } from '../../breakout-room/breakout-room/service';
|
|
|
|
import { useBreakoutExitObserver } from './hooks';
|
2023-12-12 00:21:00 +08:00
|
|
|
|
|
|
|
const intlMessages = defineMessages({
|
|
|
|
title: {
|
|
|
|
id: 'app.breakoutJoinConfirmation.title',
|
|
|
|
description: 'Join breakout room title',
|
|
|
|
},
|
|
|
|
message: {
|
|
|
|
id: 'app.breakoutJoinConfirmation.message',
|
|
|
|
description: 'Join breakout confirm message',
|
|
|
|
},
|
|
|
|
freeJoinMessage: {
|
|
|
|
id: 'app.breakoutJoinConfirmation.freeJoinMessage',
|
|
|
|
description: 'Join breakout confirm message',
|
|
|
|
},
|
|
|
|
confirmLabel: {
|
|
|
|
id: 'app.createBreakoutRoom.join',
|
|
|
|
description: 'Join confirmation button label',
|
|
|
|
},
|
|
|
|
confirmDesc: {
|
|
|
|
id: 'app.breakoutJoinConfirmation.confirmDesc',
|
|
|
|
description: 'adds context to confirm option',
|
|
|
|
},
|
|
|
|
dismissLabel: {
|
|
|
|
id: 'app.breakoutJoinConfirmation.dismissLabel',
|
|
|
|
description: 'Cancel button label',
|
|
|
|
},
|
|
|
|
dismissDesc: {
|
|
|
|
id: 'app.breakoutJoinConfirmation.dismissDesc',
|
|
|
|
description: 'adds context to dismiss option',
|
|
|
|
},
|
|
|
|
generatingURL: {
|
|
|
|
id: 'app.createBreakoutRoom.generatingURLMessage',
|
|
|
|
description: 'label for generating breakout room url',
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
interface BreakoutJoinConfirmationProps {
|
|
|
|
freeJoin: boolean;
|
|
|
|
breakouts: BreakoutRoom[];
|
|
|
|
currentUserJoined: boolean,
|
2024-04-30 21:53:47 +08:00
|
|
|
firstBreakoutId: string;
|
2024-05-21 23:31:17 +08:00
|
|
|
isUsingAudio: () => boolean;
|
|
|
|
exitVideo: () => Promise<boolean>;
|
|
|
|
exitAudio: () => Promise<unknown>;
|
|
|
|
storeVideoDevices: () => void;
|
2023-12-12 00:21:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
const BreakoutJoinConfirmation: React.FC<BreakoutJoinConfirmationProps> = ({
|
|
|
|
freeJoin,
|
|
|
|
breakouts,
|
|
|
|
currentUserJoined,
|
2024-04-30 21:53:47 +08:00
|
|
|
firstBreakoutId,
|
2024-05-21 23:31:17 +08:00
|
|
|
isUsingAudio,
|
|
|
|
exitAudio,
|
|
|
|
exitVideo,
|
|
|
|
storeVideoDevices,
|
2023-12-12 00:21:00 +08:00
|
|
|
}) => {
|
2024-04-04 08:54:32 +08:00
|
|
|
const [breakoutRoomRequestJoinURL] = useMutation(BREAKOUT_ROOM_REQUEST_JOIN_URL);
|
2024-07-05 04:00:06 +08:00
|
|
|
const [callHandleInviteDismissedAt] = useMutation(handleInviteDismissedAt);
|
2024-04-04 08:54:32 +08:00
|
|
|
|
2023-12-12 00:21:00 +08:00
|
|
|
const intl = useIntl();
|
|
|
|
const [waiting, setWaiting] = React.useState(false);
|
|
|
|
const [isOpen, setIsOpen] = React.useState(false);
|
2024-04-30 20:23:06 +08:00
|
|
|
|
2024-06-13 03:21:31 +08:00
|
|
|
const defaultSelectedBreakoutId = breakouts.find(({ isLastAssignedRoom }) => isLastAssignedRoom)?.breakoutRoomId
|
2024-05-01 02:56:30 +08:00
|
|
|
|| firstBreakoutId;
|
2024-04-30 21:53:47 +08:00
|
|
|
|
2024-04-30 20:23:06 +08:00
|
|
|
const [selectValue, setSelectValue] = React.useState(defaultSelectedBreakoutId);
|
2023-12-12 00:21:00 +08:00
|
|
|
|
2024-04-04 08:54:32 +08:00
|
|
|
const requestJoinURL = (breakoutRoomId: string) => {
|
|
|
|
breakoutRoomRequestJoinURL({ variables: { breakoutRoomId } });
|
|
|
|
};
|
2024-04-30 21:53:47 +08:00
|
|
|
|
|
|
|
// request join url if free join is enabled and user is not assigned to any room
|
|
|
|
if (defaultSelectedBreakoutId === firstBreakoutId) {
|
|
|
|
const selectedBreakout = breakouts.find(({ breakoutRoomId }) => breakoutRoomId === defaultSelectedBreakoutId);
|
|
|
|
if (!selectedBreakout?.joinURL && !waiting) {
|
|
|
|
requestJoinURL(defaultSelectedBreakoutId);
|
|
|
|
setWaiting(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-12 00:21:00 +08:00
|
|
|
const handleSelectChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
|
|
|
setSelectValue(event.target.value);
|
|
|
|
const selectedBreakout = breakouts.find(({ breakoutRoomId }) => breakoutRoomId === event.target.value);
|
|
|
|
if (!selectedBreakout?.joinURL) {
|
|
|
|
requestJoinURL(event.target.value);
|
|
|
|
setWaiting(true);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const handleJoinBreakoutConfirmation = () => {
|
2024-05-21 23:31:17 +08:00
|
|
|
if (isUsingAudio()) {
|
|
|
|
exitAudio();
|
|
|
|
logger.info(
|
|
|
|
{ logCode: 'breakout_join_confirmation' },
|
|
|
|
'Joining breakout room closed audio in the main room',
|
|
|
|
);
|
|
|
|
}
|
|
|
|
storeVideoDevices();
|
|
|
|
exitVideo();
|
2023-12-12 00:21:00 +08:00
|
|
|
if (breakouts.length === 1) {
|
|
|
|
const breakout = breakouts[0];
|
|
|
|
|
|
|
|
if (breakout?.joinURL) {
|
|
|
|
window.open(breakout.joinURL, '_blank');
|
|
|
|
}
|
|
|
|
setIsOpen(false);
|
|
|
|
} else {
|
|
|
|
const selectedBreakout = breakouts.find(({ breakoutRoomId }) => breakoutRoomId === selectValue);
|
|
|
|
if (selectedBreakout?.joinURL) {
|
|
|
|
window.open(selectedBreakout.joinURL, '_blank');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const select = useMemo(() => {
|
|
|
|
return (
|
|
|
|
<Styled.SelectParent>
|
|
|
|
{`${intl.formatMessage(intlMessages.freeJoinMessage)}`}
|
|
|
|
<Styled.Select
|
|
|
|
value={selectValue}
|
|
|
|
onChange={handleSelectChange}
|
|
|
|
disabled={waiting}
|
2024-04-23 22:57:41 +08:00
|
|
|
data-test="selectBreakoutRoomBtn"
|
2023-12-12 00:21:00 +08:00
|
|
|
>
|
|
|
|
{
|
|
|
|
breakouts.sort((a, b) => a.sequence - b.sequence).map(({ shortName, breakoutRoomId }) => (
|
|
|
|
<option
|
|
|
|
data-test="roomOption"
|
|
|
|
key={breakoutRoomId}
|
|
|
|
value={breakoutRoomId}
|
|
|
|
>
|
|
|
|
{shortName}
|
|
|
|
</option>
|
|
|
|
))
|
|
|
|
}
|
|
|
|
</Styled.Select>
|
|
|
|
{ waiting ? <span data-test="labelGeneratingURL">{intl.formatMessage(intlMessages.generatingURL)}</span> : null}
|
|
|
|
</Styled.SelectParent>
|
|
|
|
);
|
|
|
|
}, [breakouts, waiting, selectValue]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (waiting) {
|
|
|
|
const breakout = breakouts.find(({ breakoutRoomId }) => breakoutRoomId === selectValue);
|
|
|
|
if (breakout?.joinURL) {
|
|
|
|
setWaiting(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, [breakouts, waiting]);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (breakouts?.length > 0 && !currentUserJoined) {
|
|
|
|
setIsOpen(true);
|
|
|
|
}
|
|
|
|
}, [breakouts, currentUserJoined]);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<ModalFullscreen
|
|
|
|
title={intl.formatMessage(intlMessages.title)}
|
|
|
|
confirm={{
|
|
|
|
callback: handleJoinBreakoutConfirmation,
|
|
|
|
label: intl.formatMessage(intlMessages.confirmLabel),
|
|
|
|
description: intl.formatMessage(intlMessages.confirmDesc),
|
|
|
|
icon: 'popout_window',
|
|
|
|
disabled: waiting,
|
|
|
|
}}
|
|
|
|
dismiss={{
|
2024-04-23 05:51:14 +08:00
|
|
|
callback: () => {
|
|
|
|
setIsOpen(false);
|
2024-07-05 04:00:06 +08:00
|
|
|
callHandleInviteDismissedAt();
|
2024-04-23 05:51:14 +08:00
|
|
|
},
|
2023-12-12 00:21:00 +08:00
|
|
|
label: intl.formatMessage(intlMessages.dismissLabel),
|
|
|
|
description: intl.formatMessage(intlMessages.dismissDesc),
|
|
|
|
}}
|
|
|
|
{...{
|
|
|
|
setIsOpen,
|
|
|
|
isOpen,
|
|
|
|
priority: 'medium',
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{freeJoin ? select : `${intl.formatMessage(intlMessages.message)} ${breakouts[0].shortName}?`}
|
|
|
|
</ModalFullscreen>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
const BreakoutJoinConfirmationContainer: React.FC = () => {
|
2024-04-04 08:54:32 +08:00
|
|
|
const { data: currentUser } = useCurrentUser((u) => {
|
2023-12-12 00:21:00 +08:00
|
|
|
return {
|
|
|
|
isModerator: u.isModerator,
|
|
|
|
breakoutRooms: u.breakoutRooms,
|
|
|
|
};
|
|
|
|
});
|
|
|
|
const {
|
|
|
|
data: breakoutData,
|
2024-06-04 21:40:54 +08:00
|
|
|
} = useDeduplicatedSubscription<GetBreakoutDataResponse>(getBreakoutData);
|
2024-05-21 23:31:17 +08:00
|
|
|
const exitVideo = useExitVideo(true);
|
|
|
|
const { streams: videoStreams } = useStreams();
|
|
|
|
const storeVideoDevices = () => {
|
|
|
|
VideoService.storeDeviceIds(videoStreams);
|
|
|
|
};
|
|
|
|
const { exitAudio } = AudioService;
|
|
|
|
const { isUsingAudio } = AudioManager;
|
|
|
|
const breakoutExitObserver = useBreakoutExitObserver();
|
2024-05-22 01:33:47 +08:00
|
|
|
useEffect(() => {
|
|
|
|
breakoutExitObserver.setCallback('rejoinAudio', rejoinAudio);
|
|
|
|
return () => {
|
|
|
|
breakoutExitObserver.removeCallback('rejoinAudio');
|
|
|
|
};
|
|
|
|
}, []);
|
2023-12-12 00:21:00 +08:00
|
|
|
const {
|
|
|
|
data: breakoutCountData,
|
2024-06-04 21:40:54 +08:00
|
|
|
} = useDeduplicatedSubscription<GetBreakoutCountResponse>(getBreakoutCount);
|
2023-12-12 00:21:00 +08:00
|
|
|
if (!breakoutCountData || !breakoutCountData.breakoutRoom_aggregate.aggregate.count) return null;
|
|
|
|
if (!breakoutData || breakoutData.breakoutRoom.length === 0) return null;
|
|
|
|
const firstBreakout = breakoutData.breakoutRoom[0];
|
|
|
|
const {
|
|
|
|
freeJoin,
|
|
|
|
sendInvitationToModerators,
|
2024-04-30 21:53:47 +08:00
|
|
|
breakoutRoomId,
|
2023-12-12 00:21:00 +08:00
|
|
|
} = firstBreakout;
|
|
|
|
if (!sendInvitationToModerators && currentUser?.isModerator) return null;
|
|
|
|
return (
|
|
|
|
<BreakoutJoinConfirmation
|
|
|
|
freeJoin={freeJoin}
|
|
|
|
breakouts={breakoutData.breakoutRoom}
|
2024-06-13 03:21:31 +08:00
|
|
|
currentUserJoined={currentUser?.breakoutRooms?.isUserCurrentlyInRoom ?? false}
|
2024-04-30 21:53:47 +08:00
|
|
|
firstBreakoutId={breakoutRoomId}
|
2024-05-21 23:31:17 +08:00
|
|
|
isUsingAudio={isUsingAudio}
|
|
|
|
exitVideo={exitVideo}
|
|
|
|
exitAudio={exitAudio}
|
|
|
|
storeVideoDevices={storeVideoDevices}
|
2023-12-12 00:21:00 +08:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default BreakoutJoinConfirmationContainer;
|