diff --git a/services/app/apps/codebattle/.eslintrc.yml b/services/app/apps/codebattle/.eslintrc.yml
index 890c23caf..e4328ea96 100644
--- a/services/app/apps/codebattle/.eslintrc.yml
+++ b/services/app/apps/codebattle/.eslintrc.yml
@@ -19,6 +19,7 @@ extends:
rules:
no-console: 0
react/prop-types: 0
+ react/no-unused-prop-types: 0
import/no-unresolved: 0
import/no-extraneous-dependencies:
- 2
diff --git a/services/app/apps/codebattle/assets/js/app.js b/services/app/apps/codebattle/assets/js/app.js
index 2cee1a525..1c6bafeff 100644
--- a/services/app/apps/codebattle/assets/js/app.js
+++ b/services/app/apps/codebattle/assets/js/app.js
@@ -48,7 +48,6 @@ import {
renderUserPage,
renderUsersRating,
} from './widgets';
-import renderExtensionPopup from './widgets/components/ExtensionPopup';
if (process.env.NODE_ENV === 'development') {
inspect({
@@ -91,7 +90,6 @@ window.addEventListener('phx:page-loading-stop', _info => NProgress.done());
liveSocket.connect();
const builderWidgetRoot = document.getElementById('builder-widget-root');
-const extension = document.getElementById('extension');
const gameWidgetRoot = document.getElementById('game-widget-root');
const heatmapRoot = document.getElementById('heatmap-root');
const onlineRoot = document.getElementById('online-root');
@@ -107,10 +105,6 @@ const adminTournamentRoot = document.getElementById('tournament-admin-root');
const eventWidgetRoot = document.getElementById('event-widget');
const userPageRoot = document.getElementById('user-page-root');
-if (extension) {
- renderExtensionPopup(extension);
-}
-
if (gameWidgetRoot) {
renderGameWidget(gameWidgetRoot);
}
diff --git a/services/app/apps/codebattle/assets/js/widgets/components/ChatUserInfo.jsx b/services/app/apps/codebattle/assets/js/widgets/components/ChatUserInfo.jsx
new file mode 100644
index 000000000..5750e12f4
--- /dev/null
+++ b/services/app/apps/codebattle/assets/js/widgets/components/ChatUserInfo.jsx
@@ -0,0 +1,29 @@
+import React from 'react';
+
+import useHover from '../utils/useHover';
+
+import UserInfo from './UserInfo';
+
+function ChatUserInfo({ user, displayMenu, className = '' }) {
+ const [ref, hovered] = useHover();
+
+ return (
+
+
+
+ );
+}
+
+export default ChatUserInfo;
diff --git a/services/app/apps/codebattle/assets/js/widgets/components/Editor.jsx b/services/app/apps/codebattle/assets/js/widgets/components/Editor.jsx
index c4ed36e98..4a7861145 100644
--- a/services/app/apps/codebattle/assets/js/widgets/components/Editor.jsx
+++ b/services/app/apps/codebattle/assets/js/widgets/components/Editor.jsx
@@ -1,6 +1,7 @@
import React, { memo } from 'react';
import MonacoEditor, { loader } from '@monaco-editor/react';
+import PropTypes from 'prop-types';
import haskellProvider from '../config/editor/haskell';
import sassProvider from '../config/editor/sass';
@@ -64,4 +65,33 @@ function Editor(props) {
);
}
+Editor.propTypes = {
+ value: PropTypes.string.isRequired,
+ syntax: PropTypes.string,
+ onChange: PropTypes.func.isRequired,
+ theme: PropTypes.string.isRequired,
+ loading: PropTypes.bool,
+ wordWrap: PropTypes.string,
+ lineNumbers: PropTypes.string,
+ fontSize: PropTypes.number,
+ editable: PropTypes.bool,
+ gameMode: PropTypes.string.isRequired,
+ checkResult: PropTypes.func.isRequired,
+ toggleMuteSound: PropTypes.func.isRequired,
+ mute: PropTypes.bool.isRequired,
+ userType: PropTypes.string.isRequired,
+ userId: PropTypes.string.isRequired,
+ onChangeCursorSelection: PropTypes.func.isRequired,
+ onChangeCursorPosition: PropTypes.func.isRequired,
+};
+
+Editor.defaultProps = {
+ wordWrap: 'off',
+ lineNumbers: 'on',
+ syntax: 'js',
+ fontSize: 16,
+ editable: false,
+ loading: false,
+};
+
export default memo(Editor);
diff --git a/services/app/apps/codebattle/assets/js/widgets/components/ExtensionPopup.jsx b/services/app/apps/codebattle/assets/js/widgets/components/ExtensionPopup.jsx
deleted file mode 100644
index 66d8bca7b..000000000
--- a/services/app/apps/codebattle/assets/js/widgets/components/ExtensionPopup.jsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import React, { useState } from 'react';
-
-import Button from 'react-bootstrap/Button';
-import Modal from 'react-bootstrap/Modal';
-import { createRoot } from 'react-dom';
-
-const isExtensionInstalled = info => new Promise(resolve => {
- const img = new Image();
- img.src = `chrome-extension://${info.id}/${info.path}`;
- img.onload = () => {
- resolve(true);
- };
- img.onerror = () => {
- resolve(false);
- };
-});
-
-function ExtensionPopup() {
- const [modalShowing, setModalShowing] = useState(true);
- const handleHide = () => { setModalShowing(false); };
-
- return (
-
-
-
- Do you know?
-
-
-
-
- {'We have a '}
-
- chrome extension
-
- , would you like to install it?
-
-
-
-
-
-
-
- );
-}
-
-export default domElement => {
- const lastCheckExtension = window.localStorage.getItem('lastCheckExtension');
- const nowTime = Date.now();
- const threeDay = 1000 * 60 * 60 * 24 * 3;
- const isExpired = Number(lastCheckExtension) + threeDay < nowTime;
- if (window.chrome && isExpired) {
- // TODO: move to env config extension id and icon path
- const extensionInfo = { id: 'embfhnfkfobkdohleknckodkmhgmpdli', path: 'assets/128.png' };
- isExtensionInstalled(extensionInfo).then(isInstall => {
- if (!isInstall) {
- window.localStorage.setItem('lastCheckExtension', nowTime);
- createRoot(domElement).render();
- }
- });
- }
-};
diff --git a/services/app/apps/codebattle/assets/js/widgets/components/InfoMessage.jsx b/services/app/apps/codebattle/assets/js/widgets/components/InfoMessage.jsx
new file mode 100644
index 000000000..b48380e4f
--- /dev/null
+++ b/services/app/apps/codebattle/assets/js/widgets/components/InfoMessage.jsx
@@ -0,0 +1,16 @@
+import React from 'react';
+
+import moment from 'moment';
+
+function InfoMessage({ text, time }) {
+ return (
+
+ {text}
+
+ {time ? moment.unix(time).format('HH:mm:ss') : ''}
+
+
+ );
+}
+
+export default InfoMessage;
diff --git a/services/app/apps/codebattle/assets/js/widgets/components/Loading.jsx b/services/app/apps/codebattle/assets/js/widgets/components/Loading.jsx
index 63e1dc273..22b392971 100644
--- a/services/app/apps/codebattle/assets/js/widgets/components/Loading.jsx
+++ b/services/app/apps/codebattle/assets/js/widgets/components/Loading.jsx
@@ -11,14 +11,14 @@ const getSize = ({ small = false, large = false, adaptive = false }) => {
}
};
-const Loading = params => {
- const size = getSize(params);
+function Loading(props) {
+ const size = getSize(props);
return (
);
-};
+}
export default Loading;
diff --git a/services/app/apps/codebattle/assets/js/widgets/components/Message.jsx b/services/app/apps/codebattle/assets/js/widgets/components/Message.jsx
index 3a9e5bcc4..97c4186eb 100644
--- a/services/app/apps/codebattle/assets/js/widgets/components/Message.jsx
+++ b/services/app/apps/codebattle/assets/js/widgets/components/Message.jsx
@@ -1,23 +1,53 @@
import React from 'react';
import cn from 'classnames';
-import moment from 'moment';
+import useHover from '../utils/useHover';
+
+import InfoMessage from './InfoMessage';
import MessageTag from './MessageTag';
import MessageTimestamp from './MessageTimestamp';
+import SystemMessage from './SystemMessage';
+
+function MessageHeader({ name, time, hovered }) {
+ const playerClassName = cn(
+ 'd-inline-block text-truncate align-top text-nowrap cb-username-max-length mr-1',
+ { 'text-primary': hovered },
+ );
+
+ return (
+ <>
+
+
+ {name}
+
+
+
+ >
+ );
+}
+
+function MessagePart({ part, index, name }) {
+ if (part.slice(1) === name) {
+ return (
+
+ {part}
+
+ );
+ }
-const MessageHeader = ({ name, time }) => (
- <>
-
-
- {name}
+ if (part.startsWith('@')) {
+ return (
+
+ {part}
-
-
- >
-);
+ );
+ }
+
+ return part;
+}
-const Message = ({
+function Message({
text = '',
name = '',
userId,
@@ -25,84 +55,58 @@ const Message = ({
time,
meta,
displayMenu,
-}) => {
+}) {
+ const [chatHeaderRef, hoveredChatHeader] = useHover();
+
if (!text) {
return null;
}
if (type === 'system') {
- const statusClassName = cn('text-small', {
- 'text-danger': ['error', 'failure'].includes(meta?.status),
- 'text-success': meta?.status === 'success',
- 'text-muted': meta?.status === 'event',
- });
-
- return (
-
- {text}
-
- );
+ return ;
}
if (type === 'info') {
- return (
-
- {text}
-
- {time ? moment.unix(time).format('HH:mm:ss') : ''}
-
-
- );
+ return ;
}
const parts = text.split(/(@+[-a-zA-Z0-9_]+\b)/g);
- const renderMessagePart = (part, i) => {
- if (part.slice(1) === name) {
- return (
-
- {part}
-
- );
- }
- if (part.startsWith('@')) {
- return (
-
- {part}
-
- );
- }
- return part;
- };
-
const textPartsClassNames = cn('text-break', {
'cb-private-text': meta?.type === 'private',
});
return (
-
+
-
+
- {parts.map((part, i) => renderMessagePart(part, i))}
+ {parts.map(
+ (part, i) => (
+ /* eslint-disable react/no-array-index-key */
+
+ ),
+ )}
);
-};
+}
export default Message;
diff --git a/services/app/apps/codebattle/assets/js/widgets/components/Messages.jsx b/services/app/apps/codebattle/assets/js/widgets/components/Messages.jsx
index 7e2d70505..1608512b9 100644
--- a/services/app/apps/codebattle/assets/js/widgets/components/Messages.jsx
+++ b/services/app/apps/codebattle/assets/js/widgets/components/Messages.jsx
@@ -6,12 +6,12 @@ import useStayScrolled from '../utils/useStayScrolled';
import Message from './Message';
-const getKey = (id, time, name) => {
+const getKey = (id, time, name, index) => {
if (!time || !name) {
return id;
}
- return `${id}-${time}-${name}`;
+ return `${id}-${time}-${name}-${index}`;
};
function Messages({ messages, displayMenu = () => {}, disabled = false }) {
@@ -41,12 +41,12 @@ function Messages({ messages, displayMenu = () => {}, disabled = false }) {
ref={listRef}
className="overflow-auto pt-0 pl-3 pr-2 position-relative cb-messages-list flex-grow-1"
>
- {messages.map(message => {
+ {messages.map((message, index) => {
const {
id, userId, name, text, type, time, meta,
} = message;
- const key = getKey(id, time, name);
+ const key = getKey(id, time, name, messages.length - index);
return (
+ {text}
+
+ );
+}
+
+export default SystemMessage;
diff --git a/services/app/apps/codebattle/assets/js/widgets/components/UserInfo.jsx b/services/app/apps/codebattle/assets/js/widgets/components/UserInfo.jsx
index 619dcf38d..86d130c15 100644
--- a/services/app/apps/codebattle/assets/js/widgets/components/UserInfo.jsx
+++ b/services/app/apps/codebattle/assets/js/widgets/components/UserInfo.jsx
@@ -48,6 +48,7 @@ function UserInfo({
className,
user,
lang,
+ hovered = false,
hideLink = false,
hideInfo = false,
truncate = false,
@@ -56,6 +57,7 @@ function UserInfo({
placement = Placements.bottomStart,
}) {
const { presenceList } = useSelector(selectors.lobbyDataSelector);
+ const isAdmin = useSelector(selectors.userIsAdminSelector(user?.id));
const content = useMemo(() => , [user]);
if (!user?.id) {
@@ -75,9 +77,11 @@ function UserInfo({
return (
{
const commonClassName = 'd-flex align-items-center';
const onlineIndicatorClassName = cn('mr-1', {
@@ -15,11 +15,11 @@ const UserName = ({
'cb-user-offline': !isOnline,
});
const userClassName = cn('text-truncate', {
- 'text-danger': user.isAdmin,
'x-username-truncated': truncate,
});
-
- const userName = user.rank ? `${user.name}(${user.rank})` : user.name;
+ const userNameClassName = cn({
+ 'text-primary': hovered,
+ });
return (
);
diff --git a/services/app/apps/codebattle/assets/js/widgets/middlewares/Room.js b/services/app/apps/codebattle/assets/js/widgets/middlewares/Room.js
index fbd57e997..5db8e33d4 100644
--- a/services/app/apps/codebattle/assets/js/widgets/middlewares/Room.js
+++ b/services/app/apps/codebattle/assets/js/widgets/middlewares/Room.js
@@ -213,36 +213,21 @@ export const updateEditorText = (editorText, langSlug = null) => (dispatch, getS
);
};
-export const sendEditorLang = langSlug => (dispatch, getState) => {
+export const sendEditorLang = langSlug => (_dispatch, getState) => {
const state = getState();
const userId = selectors.currentUserIdSelector(state);
const currentLangSlug = langSlug || selectors.userLangSelector(userId)(state);
- dispatch(
- actions.updateEditorText({
- userId,
- langSlug: currentLangSlug,
- }),
- );
-
channel.push(channelMethods.editorLang, {
langSlug: currentLangSlug,
});
};
-export const sendEditorText = (editorText, langSlug = null) => (dispatch, getState) => {
+export const sendEditorText = (editorText, langSlug = null) => (_dispatch, getState) => {
const state = getState();
const userId = selectors.currentUserIdSelector(state);
const currentLangSlug = langSlug || selectors.userLangSelector(userId)(state);
- dispatch(
- actions.updateEditorText({
- userId,
- editorText,
- langSlug: currentLangSlug,
- }),
- );
-
channel.push(channelMethods.editorData, {
editorText,
langSlug: currentLangSlug,
@@ -309,6 +294,18 @@ export const sendCurrentLangAndSetTemplate = langSlug => (dispatch, getState) =>
const currentText = selectors.currentPlayerTextByLangSelector(langSlug)(state);
const { solutionTemplate: template } = find(langs, { slug: langSlug });
const textToSet = currentText || template;
+
+ const userId = selectors.currentUserIdSelector(state);
+ const newLangSlug = langSlug || selectors.userLangSelector(userId)(state);
+
+ dispatch(
+ actions.updateEditorText({
+ userId,
+ editorText: textToSet,
+ langSlug: newLangSlug,
+ }),
+ );
+
dispatch(sendEditorText(textToSet, langSlug));
dispatch(sendEditorLang(langSlug));
};
@@ -324,13 +321,14 @@ export const resetTextToTemplateAndSend = langSlug => (dispatch, getState) => {
const state = getState();
const langs = selectors.editorLangsSelector(state) || defaultLanguages;
const { solutionTemplate: template } = find(langs, { slug: langSlug });
+ dispatch(updateEditorText(template, langSlug));
dispatch(sendEditorText(template, langSlug));
};
export const soundNotification = notification();
export const addCursorListeners = (userId, onChangePosition, onChangeSelection) => {
- if (!userId) {
+ if (!userId || isRecord) {
return () => {};
}
diff --git a/services/app/apps/codebattle/assets/js/widgets/pages/RoomWidget.jsx b/services/app/apps/codebattle/assets/js/widgets/pages/RoomWidget.jsx
index 1513df86b..82fe7d0c1 100644
--- a/services/app/apps/codebattle/assets/js/widgets/pages/RoomWidget.jsx
+++ b/services/app/apps/codebattle/assets/js/widgets/pages/RoomWidget.jsx
@@ -58,14 +58,14 @@ function PanelsSplitPane({ children, viewMode }) {
return (
- {children[0]}
+ {children[0]}
{children[1]}
);
diff --git a/services/app/apps/codebattle/assets/js/widgets/pages/game/ChatWidget.jsx b/services/app/apps/codebattle/assets/js/widgets/pages/game/ChatWidget.jsx
index 9991b6e7c..fb1939435 100644
--- a/services/app/apps/codebattle/assets/js/widgets/pages/game/ChatWidget.jsx
+++ b/services/app/apps/codebattle/assets/js/widgets/pages/game/ChatWidget.jsx
@@ -1,20 +1,20 @@
import React, {
useContext,
- useMemo,
+ // useMemo,
useRef,
} from 'react';
import cn from 'classnames';
-import filter from 'lodash/filter';
-import uniqBy from 'lodash/uniqBy';
+// import filter from 'lodash/filter';
+// import uniqBy from 'lodash/uniqBy';
import { useSelector } from 'react-redux';
import ChatContextMenu from '../../components/ChatContextMenu';
import ChatHeader from '../../components/ChatHeader';
import ChatInput from '../../components/ChatInput';
+// import ChatUserInfo from '../../components/ChatUserInfo';
import Messages from '../../components/Messages';
import RoomContext from '../../components/RoomContext';
-import UserInfo from '../../components/UserInfo';
import GameRoomModes from '../../config/gameModes';
import {
inTestingRoomSelector,
@@ -43,10 +43,10 @@ function ChatWidget() {
const isTestingRoom = useMachineStateSelector(mainService, inTestingRoomSelector);
const isRestricted = useMachineStateSelector(mainService, isRestrictedContentSelector);
- const isTournamentGame = (gameMode === GameRoomModes.tournament);
+ // const isTournamentGame = (gameMode === GameRoomModes.tournament);
const isStandardGame = (gameMode === GameRoomModes.standard);
const showChatInput = !openedReplayer && !isTestingRoom && useChat && !isRestricted;
- const showChatParticipants = !isTestingRoom && useChat && !isRestricted;
+ // const showChatParticipants = !isTestingRoom && useChat && !isRestricted;
const disabledChatHeader = isTestingRoom || !isOnline || !useChat;
const disabledChatMessages = isTestingRoom || !useChat || isRestricted;
@@ -62,10 +62,10 @@ function ChatWidget() {
useChatRooms('page');
- const listOfUsers = useMemo(() => {
- const uniqUsers = uniqBy(users, 'id');
- return isTournamentGame ? filter(uniqUsers, { isBot: false }) : uniqUsers;
- }, [isTournamentGame, users]);
+ // const listOfUsers = useMemo(() => {
+ // const uniqUsers = uniqBy(users, 'id');
+ // return isTournamentGame ? filter(uniqUsers, { isBot: false }) : uniqUsers;
+ // }, [isTournamentGame, users]);
const activeRoom = useSelector(selectors.activeRoomSelector);
const filteredMessages = messages.filter(message => shouldShowMessage(message, activeRoom));
@@ -104,29 +104,14 @@ function ChatWidget() {
- {showChatParticipants && (
-
-
- {`Online players: ${listOfUsers.length}`}
-
- {listOfUsers.map(user => (
-
-
-
- ))}
-
- )}
+ {/* {showChatParticipants && ( */}
+ {/* */}
+ {/*
*/}
+ {/* {`Online players: ${listOfUsers.length}`} */}
+ {/*
*/}
+ {/* {listOfUsers.map(user =>
)} */}
+ {/*
*/}
+ {/* )} */}
diff --git a/services/app/apps/codebattle/assets/js/widgets/pages/game/CodebattlePlayer.jsx b/services/app/apps/codebattle/assets/js/widgets/pages/game/CodebattlePlayer.jsx
index b0e63674a..707bceaf0 100644
--- a/services/app/apps/codebattle/assets/js/widgets/pages/game/CodebattlePlayer.jsx
+++ b/services/app/apps/codebattle/assets/js/widgets/pages/game/CodebattlePlayer.jsx
@@ -230,8 +230,10 @@ class CodebattlePlayer extends Component {
onIntentEnd={this.onSliderHandleChangeIntentEnd}
>
(
-
-);
+function SliderBar({ value, className }) {
+ return (
+
+ );
+}
-const SliderAction = ({
- value, className, setGameState, event,
-}) => (
-
-
- {`Check started by ${event.userName}`}
-
+function SliderAction({
+ value,
+ className,
+ event,
+ setGameState,
+}) {
+ return (
+
+
+ {`Check started by ${event.userName}`}
+
)}
- >
- {
- setGameState(value);
- }}
- className={className}
- style={{
+ >
+
{
+ setGameState(value);
+ }}
+ className={className}
+ style={{
left: `${value * 100}%`,
}}
- />
-
-
+ />
+
+
);
+}
-const SliderHandle = ({ value, className }) => (
-
-);
+function SliderHandle({ value, className }) {
+ return (
+
+ );
+}
-const CodebattleSliderBar = ({
- roomMachineState, handlerPosition, lastIntent, mainEvents, recordsCount, setGameState,
-}) => (
- <>
-
- {
- roomMachineState.matches({ replayer: replayerMachineStates.holded })
- && (
-
-)
- }
-
+
+
+
+ {mainEvents.map(event => (
+
+ ))}
+
-
- {mainEvents.map(event => (
-
- ))}
-
- >
-);
+ >
+ );
+}
export default CodebattleSliderBar;
diff --git a/services/app/apps/codebattle/assets/js/widgets/pages/game/EditorContainer.jsx b/services/app/apps/codebattle/assets/js/widgets/pages/game/EditorContainer.jsx
index 294cdb8bf..ea3cbad5e 100644
--- a/services/app/apps/codebattle/assets/js/widgets/pages/game/EditorContainer.jsx
+++ b/services/app/apps/codebattle/assets/js/widgets/pages/game/EditorContainer.jsx
@@ -89,7 +89,10 @@ function EditorContainer({
const currentEditorLangSlug = useSelector(selectors.userLangSelector(currentUserId));
const updateEditorValue = useCallback(data => dispatch(GameActions.updateEditorText(data)), [dispatch]);
- const sendEditorValue = useCallback(data => dispatch(GameActions.sendEditorText(data)), [dispatch]);
+ const updateAndSendEditorValue = useCallback(data => {
+ dispatch(GameActions.updateEditorText(data));
+ dispatch(GameActions.sendEditorText(data));
+ }, [dispatch]);
const { mainService } = useContext(RoomContext);
const isPreview = useMachineStateSelector(mainService, inPreviewRoomSelector);
@@ -194,7 +197,7 @@ function EditorContainer({
const canChange = userSettings.type === editorUserTypes.currentUser && !openedReplayer;
const editable = !openedReplayer && userSettings.editable && userSettings.editorState !== 'banned';
const canSendCursor = canChange && !inTestingRoom && !inBuilderRoom;
- const updateEditor = editorCurrent.context.editorState === 'testing' ? updateEditorValue : sendEditorValue;
+ const updateEditor = editorCurrent.context.editorState === 'testing' ? updateEditorValue : updateAndSendEditorValue;
const onChange = canChange ? updateEditor : noop;
const onChangeCursorSelection = canSendCursor ? GameActions.sendEditorCursorSelection : noop;
const onChangeCursorPosition = canSendCursor ? GameActions.sendEditorCursorPosition : noop;
diff --git a/services/app/apps/codebattle/assets/js/widgets/pages/game/GameActionButtons.jsx b/services/app/apps/codebattle/assets/js/widgets/pages/game/GameActionButtons.jsx
index 0dbbb5b3f..f343782e1 100644
--- a/services/app/apps/codebattle/assets/js/widgets/pages/game/GameActionButtons.jsx
+++ b/services/app/apps/codebattle/assets/js/widgets/pages/game/GameActionButtons.jsx
@@ -2,6 +2,7 @@ import React, { useContext, useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import i18next from 'i18next';
+import { Dropdown } from 'react-bootstrap';
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
import { useDispatch } from 'react-redux';
@@ -74,68 +75,54 @@ function CheckResultButton({ onClick, status }) {
}
}
-function GiveUpButton({ onClick, status }) {
- const dispatch = useDispatch();
+const CustomToggle = React.forwardRef(({ onClick, className, disabled }, ref) => (
+
+));
+
+function GiveUpButtonDropdownItem({ onSelect, status }) {
const commonProps = {
- type: 'button',
- className: 'btn btn-outline-danger rounded-lg',
+ key: 'giveUp',
+ href: '#',
title: i18next.t('Give Up'),
- onClick,
- 'data-toggle': 'tooltip',
- 'data-placement': 'top',
- 'data-guide-id': 'GiveUpButton',
+ onSelect,
+ disabled: status === 'disabled',
};
- switch (status) {
- case 'enabled':
- return (
-
- );
- case 'disabled':
- return (
-
- );
- default: {
- dispatch(actions.setError(new Error('unnexpected give up status')));
- return null;
- }
- }
+ return (
+
+
+
+ Give up
+
+
+ );
}
-function ResetButton({ onClick, status }) {
- const dispatch = useDispatch();
+function ResetButtonDropDownItem({ onSelect, status }) {
const commonProps = {
- type: 'button',
- className: 'btn btn-outline-secondary rounded-lg mx-1',
+ key: 'reset',
+ href: '#',
title: i18next.t('Reset solution'),
- onClick,
- 'data-toggle': 'tooltip',
- 'data-placement': 'top',
- 'data-guide-id': 'ResetButton',
+ onSelect,
+ disabled: status === 'disabled',
};
- switch (status) {
- case 'enabled':
- return (
-
- );
- case 'disabled':
- return (
-
- );
- default: {
- dispatch(actions.setError(new Error('unnexpected reset status')));
- return null;
- }
- }
+ return (
+
+
+
+ Reset Solution
+
+
+ );
}
function GameActionButtons({
@@ -199,11 +186,23 @@ function GameActionButtons({
role="group"
aria-label="Game actions"
>
- {showGiveUpBtn && (
-
- )}
-
+
+
+
+
+
+
+
+ {showGiveUpBtn && }
+
+
{renderModal()}
);
diff --git a/services/app/apps/codebattle/assets/js/widgets/pages/game/TaskAssignment.jsx b/services/app/apps/codebattle/assets/js/widgets/pages/game/TaskAssignment.jsx
index 06b393b09..468f524c8 100644
--- a/services/app/apps/codebattle/assets/js/widgets/pages/game/TaskAssignment.jsx
+++ b/services/app/apps/codebattle/assets/js/widgets/pages/game/TaskAssignment.jsx
@@ -116,7 +116,7 @@ function TaskAssignment({
{!fullSize && (