Skip to content

Commit

Permalink
add video conference and small fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ReDBrother committed Jan 8, 2025
1 parent 5aaa40f commit e827c58
Show file tree
Hide file tree
Showing 14 changed files with 284 additions and 26 deletions.
3 changes: 3 additions & 0 deletions services/app/apps/codebattle/assets/js/widgets/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ const {
const { gameUI: gameUIReducer, ...otherReducers } = reducers;

const gameUIPersistWhitelist = [
'audioMute',
'videoMute',
'showVideoConferencePanel',
'editorMode',
'editorTheme',
'streamMode',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import useChatRooms from '../../utils/useChatRooms';
import useMachineStateSelector from '../../utils/useMachineStateSelector';

import Notifications from './Notifications';
import VideoConference from './VideoConference';

function ChatWidget() {
const { mainService } = useContext(RoomContext);
Expand All @@ -38,14 +39,15 @@ function ChatWidget() {
const historyMessages = useSelector(selectors.chatHistoryMessagesSelector);
const gameMode = useSelector(selectors.gameModeSelector);
const useChat = useSelector(selectors.gameUseChatSelector);
const showVideoConferencePanel = useSelector(selectors.showVideoConferencePanelSelector);

const openedReplayer = useMachineStateSelector(mainService, openedReplayerSelector);
const isTestingRoom = useMachineStateSelector(mainService, inTestingRoomSelector);
const isRestricted = useMachineStateSelector(mainService, isRestrictedContentSelector);

// const isTournamentGame = (gameMode === GameRoomModes.tournament);
const isStandardGame = (gameMode === GameRoomModes.standard);
const showChatInput = !openedReplayer && !isTestingRoom && useChat && !isRestricted;
const showChatInput = !openedReplayer && !isTestingRoom && !isRestricted && useChat;
// const showChatParticipants = !isTestingRoom && useChat && !isRestricted;

const disabledChatHeader = isTestingRoom || !isOnline || !useChat;
Expand Down Expand Up @@ -83,21 +85,27 @@ function ChatWidget() {
'cb-game-chat-container cb-messages-container',
)}
>
<ChatHeader showRooms={isStandardGame} disabled={disabledChatHeader} />
{openedReplayer
? (
<Messages
messages={historyMessages}
disabled={disabledChatMessages}
/>
) : (
<Messages
displayMenu={displayMenu}
messages={filteredMessages}
disabled={disabledChatMessages}
/>
{showVideoConferencePanel ? (
<VideoConference />
) : (
<>
<ChatHeader showRooms={isStandardGame} disabled={disabledChatHeader} />
{openedReplayer
? (
<Messages
messages={historyMessages}
disabled={disabledChatMessages}
/>
) : (
<Messages
displayMenu={displayMenu}
messages={filteredMessages}
disabled={disabledChatMessages}
/>
)}
{showChatInput && <ChatInput inputRef={inputRef} disabled={disabledChatInput} />}
</>
)}
{showChatInput && <ChatInput inputRef={inputRef} disabled={disabledChatInput} />}
</div>
<div className="flex-shrink-1 p-0 border-left rounded-right cb-game-control-container">
<div className="d-flex flex-column justify-content-start overflow-auto h-100">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import BackToTournamentButton from './BackToTournamentButton';
import GameResult from './GameResult';
import GoToNextGame from './GoToNextGame';
import ReplayerControlButton from './ReplayerControlButton';
import VideoConferenceButton from './VideoConferenceButton';

function Notifications() {
const { mainService } = useContext(RoomContext);
Expand All @@ -35,6 +36,10 @@ function Notifications() {
return (
<>
{roomMachineState.matches({ room: roomMachineStates.testing }) && <BackToTaskBuilderButton />}
{(isAdmin
&& !roomMachineState.matches({ replayer: replayerMachineStates.off })
&& !roomMachineState.matches({ room: roomMachineStates.testing })
) && <VideoConferenceButton />}
<ReplayerControlButton />
{(isCurrentUserPlayer && roomMachineState.matches({ room: roomMachineStates.gameOver }))
&& (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, {
memo,
} from 'react';

import cn from 'classnames';

import Loading from '@/components/Loading';
import useJitsiRoom from '@/utils/useJitsiRoom';

import i18n from '../../../i18n';

const mapStatusToDescription = {
loading: i18n.t('Setup Conference Room'),
ready: i18n.t('Conference Room Is Ready'),
joinedGameRoom: i18n.t('Conference Room Is Started'),
notSupported: i18n.t('Not Supported Browser'),
noHaveApiKey: i18n.t('No have jitsi api key'),
};

function ConferenceLoading({ status, hideLoader = false }) {
return (
<div className="d-flex flex-column">
{!hideLoader && <Loading />}
<small>{mapStatusToDescription[status]}</small>
</div>
);
}

function VideoConference() {
const {
ref,
status,
} = useJitsiRoom();

const conferenceClassName = cn('w-100 h-100', {
'd-none invisible absolute': status !== 'joinedGameRoom',
});

return (
<>
{status !== 'joinedGameRoom' && (
<div className="d-flex w-100 h-100 justify-content-center align-items-center">
<ConferenceLoading
status={status}
hideLoader={['notSupported', 'noHaveApiKey'].includes(status)}
/>
</div>
)}
<div ref={ref} id="jaas-container" className={conferenceClassName} />
</>
);
}

export default memo(VideoConference);
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* global JitsiMeetExternalAPI */
import React from 'react';

import { useDispatch, useSelector } from 'react-redux';

import { actions } from '@/slices';

import i18n from '../../../i18n';
import * as selectors from '../../selectors';

function VideoConferenceButton() {
const dispatch = useDispatch();

// const { audioMute, videoMute } = useSelector(selectors.videoConferenceSettingsSelector);
const showVideoConferencePanel = useSelector(selectors.showVideoConferencePanelSelector);

const toggleVideoConference = () => {
dispatch(actions.toggleShowVideoConferencePanel());
};

if (!JitsiMeetExternalAPI) {
return <></>;
}

return (
<>
<button
type="button"
onClick={toggleVideoConference}
className="btn btn-secondary btn-block rounded-lg"
aria-label={
showVideoConferencePanel
? 'Open Text Chat'
: 'Open Video Chat'
}
>
{
showVideoConferencePanel
? i18n.t('Open Text Chat')
: i18n.t('Open Video Chat')
}
</button>
{/* {showVideoConferencePanel && ( */}
{/* <div className="d-flex"> */}
{/* <button */}
{/* type="button" */}
{/* className="btn btn-secondary btn-block w-100 rounded-lg" */}
{/* aria-label="Mute audio" */}
{/* /> */}
{/* <button */}
{/* type="button" */}
{/* className="btn btn-secondary btn-block w-100 rounded-lg" */}
{/* aria-label="Mute video" */}
{/* /> */}
{/* </div> */}
{/* )} */}
</>
);
}

export default VideoConferenceButton;
11 changes: 11 additions & 0 deletions services/app/apps/codebattle/assets/js/widgets/selectors/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,17 @@ export const currentChatUserSelector = state => {

export const taskDescriptionLanguageSelector = state => state.gameUI.taskDescriptionLanguage;

export const videoConferenceSettingsSelector = createDraftSafeSelector(
state => state.gameUI.audioMute,
state => state.gameUI.videoMute,
(audioMute, videoMute) => ({
audioMute,
videoMute,
}),
);

export const showVideoConferencePanelSelector = state => state.gameUI.showVideoConferencePanel;

export const playbookStatusSelector = state => state.playbook.state;

export const playbookInitRecordsSelector = state => state.playbook.initRecords;
Expand Down
12 changes: 12 additions & 0 deletions services/app/apps/codebattle/assets/js/widgets/slices/gameUI.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ const initialState = {
taskDescriptionLanguage: taskDescriptionLanguages.default,
showToastActionsAfterGame: false,
isShowGuide: false,
showVideoConferencePanel: false,
videoMute: true,
audioMute: true,
};

const gameUI = createSlice({
Expand Down Expand Up @@ -46,6 +49,15 @@ const gameUI = createSlice({
toggleStreamMode: state => {
state.streamMode = !state.streamMode;
},
toggleShowVideoConferencePanel: state => {
state.showVideoConferencePanel = !state.showVideoConferencePanel;
},
setAudioMute: (state, payload) => {
state.audioMute = payload;
},
setVideoMute: (state, payload) => {
state.videoMute = payload;
},
},
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/* global JitsiMeetExternalAPI */
import {
useEffect, useMemo, useRef, useState,
} from 'react';

import Gon from 'gon';
import { useDispatch, useSelector } from 'react-redux';

import { actions } from '@/slices';

import * as selectors from '../selectors';

const apiKey = Gon.getAsset('jitsi_api_key');

const useJitsiRoom = () => {
const dispatch = useDispatch();

const ref = useRef();
const [status, setStatus] = useState('loading');
const userId = useSelector(selectors.currentUserIdSelector);
const gameId = useSelector(selectors.gameIdSelector);
const { name } = useSelector(state => state.user.users[userId]);

const roomName = gameId ? `${apiKey}/codebattle_game_${gameId}` : `${apiKey}/codebattle_testing`;

useEffect(() => {
if (!JitsiMeetExternalAPI) {
dispatch(actions.toggleShowVideoConferencePanel());
}

if (!apiKey) {
setStatus('noHaveApiKey');
}
}, [dispatch]);

useEffect(() => {
if (status === 'loading' && JitsiMeetExternalAPI && apiKey) {
const newApi = new JitsiMeetExternalAPI('8x8.vc', {
roomName,
parentNode: ref.current,
userInfo: {
displayName: name,
},
configOverwrite: {
prejoinPageEnabled: false,
hideConferenceSubject: true,
// hideConferenceTimer: true,
toolbarButtons: [
'camera',
'microphone',
'settings',
],
},
});

newApi.addListener('browserSupport', payload => {
if (payload.supported) {
setStatus('ready');
} else {
setStatus('notSupported');
}
});

newApi.addListener('videoConferenceJoined', () => {
setStatus('joinedGameRoom');
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [status]);

return useMemo(() => ({
ref,
status,
}), [ref, status]);
};

export default useJitsiRoom;
14 changes: 11 additions & 3 deletions services/app/apps/codebattle/lib/codebattle/user_game_report.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ defmodule Codebattle.UserGameReport do
:comment,
:game_id,
:id,
:offender_id,
:offender,
:reason,
:reporter_id,
:reporter,
:state,
:tournament_id
Expand Down Expand Up @@ -79,8 +81,14 @@ defmodule Codebattle.UserGameReport do
end

def create(params) do
%__MODULE__{}
|> changeset(params)
|> Repo.insert()
result =
%__MODULE__{}
|> changeset(params)
|> Repo.insert()

case result do
{:ok, report} -> {:ok, Repo.preload(report, [:offender, :reporter])}
_ -> result
end
end
end
Loading

0 comments on commit e827c58

Please sign in to comment.