From b2420620ed06158a619ca0d8104b71bfbbece6ed Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Mon, 5 Apr 2021 18:19:50 -0700 Subject: [PATCH 01/37] First code to add numProblems sizer --- frontend/src/api/Room.ts | 2 ++ frontend/src/views/Lobby.tsx | 62 ++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/frontend/src/api/Room.ts b/frontend/src/api/Room.ts index d276130e0..ccdcf3d58 100644 --- a/frontend/src/api/Room.ts +++ b/frontend/src/api/Room.ts @@ -13,6 +13,7 @@ export type Room = { difficulty: Difficulty, duration: number, size: number, + numProblems: number, }; export type CreateRoomParams = { @@ -28,6 +29,7 @@ export type UpdateSettingsParams = { difficulty?: Difficulty, duration?: number, size?: number, + numProblems?: number, }; export type ChangeHostParams = { diff --git a/frontend/src/views/Lobby.tsx b/frontend/src/views/Lobby.tsx index 2fb350028..9325b1523 100644 --- a/frontend/src/views/Lobby.tsx +++ b/frontend/src/views/Lobby.tsx @@ -143,6 +143,7 @@ function LobbyPage() { const [difficulty, setDifficulty] = useState(null); const [duration, setDuration] = useState(15); const [size, setSize] = useState(10); + const [numProblems, setNumProblems] = useState(1); const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }); const [hoverVisible, setHoverVisible] = useState(false); @@ -174,6 +175,7 @@ function LobbyPage() { setDifficulty(room.difficulty); setDuration(room.duration / 60); setSize(room.size); + setNumProblems(room.numProblems); }; // Function to determine if the given user is the host or not @@ -374,6 +376,25 @@ function LobbyPage() { }); }; + const updateNumProblems = () => { + setLoading(true); + const prevNumProblems = numProblems; + const settings = { + initiator: currentUser!, + numProblems, + }; + + updateRoomSettings(currentRoomId, settings) + .then(() => setLoading(false)) + .then(() => setError('')) + .catch((err) => { + setLoading(false); + setError(err.message); + // Set numProblems back to original if REST call failed + setSize(prevNumProblems); + }); + }; + /** * Display the passed-in list of users on the UI, either as * active or inactive. @@ -718,6 +739,47 @@ function LobbyPage() { /> + Number of Problems + + {`${numProblems} problem${numProblems === 1 ? '' : 's'}`} + + + { + if (!isHost(currentUser)) { + setHoverVisible(true); + } + }} + onMouseLeave={() => { + if (!isHost(currentUser)) { + setHoverVisible(false); + } + }} + /> + + { + const { value } = e.target; + + // Set numProblems to undefined to allow users to clear field + if (!value) { + setNumProblems(undefined); + } else { + const newNumProblems = Number(value); + if (newNumProblems >= 1 && newNumProblems <= 8) { + setNumProblems(newNumProblems); + } + } + }} + onMouseUp={updateNumProblems} + /> + + From 8b5a2423276d587f142b6395480d837ac208c552 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Tue, 6 Apr 2021 00:22:14 -0700 Subject: [PATCH 02/37] adding tests --- frontend/src/api/Game.ts | 2 +- frontend/src/views/Lobby.tsx | 8 ++--- .../service/GameManagementServiceTests.java | 33 +++++++++++++++++++ .../main/service/RoomServiceTests.java | 25 ++++++++++++++ 4 files changed, 63 insertions(+), 5 deletions(-) diff --git a/frontend/src/api/Game.ts b/frontend/src/api/Game.ts index a90f2fb13..cdd5844d3 100644 --- a/frontend/src/api/Game.ts +++ b/frontend/src/api/Game.ts @@ -115,4 +115,4 @@ export const playAgain = (roomId: string, params: PlayAgainParams): .then((res) => res.data) .catch((err) => { throw axiosErrorHandler(err); - }); + }); \ No newline at end of file diff --git a/frontend/src/views/Lobby.tsx b/frontend/src/views/Lobby.tsx index 05e6869fd..b2244c6fa 100644 --- a/frontend/src/views/Lobby.tsx +++ b/frontend/src/views/Lobby.tsx @@ -389,6 +389,7 @@ function LobbyPage() { }; const updateNumProblems = () => { + setError('') setLoading(true); const prevNumProblems = numProblems; const settings = { @@ -398,12 +399,11 @@ function LobbyPage() { updateRoomSettings(currentRoomId, settings) .then(() => setLoading(false)) - .then(() => setError('')) .catch((err) => { setLoading(false); setError(err.message); // Set numProblems back to original if REST call failed - setSize(prevNumProblems); + setNumProblems(prevNumProblems); }); }; @@ -753,7 +753,7 @@ function LobbyPage() { { @@ -764,7 +764,7 @@ function LobbyPage() { setNumProblems(undefined); } else { const newNumProblems = Number(value); - if (newNumProblems >= 1 && newNumProblems <= 8) { + if (newNumProblems >= 1 && newNumProblems <= 10) { setNumProblems(newNumProblems); } } diff --git a/src/test/java/com/rocketden/main/service/GameManagementServiceTests.java b/src/test/java/com/rocketden/main/service/GameManagementServiceTests.java index 9243cf67c..04c43103c 100644 --- a/src/test/java/com/rocketden/main/service/GameManagementServiceTests.java +++ b/src/test/java/com/rocketden/main/service/GameManagementServiceTests.java @@ -174,6 +174,39 @@ public void startGameSuccess() { assertEquals(room.getDuration(), game.getGameTimer().getDuration()); } + @Test + public void startGameWithMultipleQuestionsSuccess() { + User host = new User(); + host.setNickname(NICKNAME); + host.setUserId(USER_ID); + + Room room = new Room(); + room.setRoomId(ROOM_ID); + room.setHost(host); + room.setDifficulty(ProblemDifficulty.RANDOM); + room.setDuration(DURATION); + room.setNumProblems(6); + + StartGameRequest request = new StartGameRequest(); + request.setInitiator(UserMapper.toDto(host)); + + Mockito.doReturn(room).when(repository).findRoomByRoomId(ROOM_ID); + RoomDto response = gameService.startGame(ROOM_ID, request); + + verify(socketService).sendSocketUpdate(eq(response)); + + assertEquals(ROOM_ID, response.getRoomId()); + assertTrue(response.isActive()); + + // Game object is created when the room chooses to start + Game game = gameService.getGameFromRoomId(ROOM_ID); + assertNotNull(game); + + assertNotNull(game.getGameTimer()); + assertEquals(room.getDuration(), game.getGameTimer().getDuration()); + assertEquals(room.getNumProblems(), response.getNumProblems()); + } + @Test public void startGameRoomNotFound() { UserDto user = new UserDto(); diff --git a/src/test/java/com/rocketden/main/service/RoomServiceTests.java b/src/test/java/com/rocketden/main/service/RoomServiceTests.java index 3f39c9a8c..139557408 100644 --- a/src/test/java/com/rocketden/main/service/RoomServiceTests.java +++ b/src/test/java/com/rocketden/main/service/RoomServiceTests.java @@ -285,6 +285,31 @@ public void manyUsersJoiningAnInfinitelySizedRoomSuccess() { assertEquals(102, room.getUsers().size()); } + @Test + public void setInvalidNumProblemsFailure() { + /** + * Verify update settings request fails when numProblems is + * set to outside of the allowable range + */ + User host = new User(); + host.setNickname(NICKNAME); + + Room room = new Room(); + room.setRoomId(ROOM_ID); + room.setHost(host); + room.addUser(host); + + UpdateSettingsRequest request = new UpdateSettingsRequest(); + request.setInitiator(UserMapper.toDto(host)); + request.setNumProblems(15); + + // Mock repository to return room when called + Mockito.doReturn(room).when(repository).findRoomByRoomId(eq(ROOM_ID)); + ApiException exception = assertThrows(ApiException.class, () -> roomService.updateRoomSettings(ROOM_ID, request)); + + verify(repository).findRoomByRoomId(ROOM_ID); + assertEquals(ProblemError.INVALID_NUMBER_REQUEST, exception.getError()); + } @Test public void getRoomSuccess() { From 5327ac690b6b60345ed5987d37f4bf5d77e12bce Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Tue, 13 Apr 2021 13:40:27 -0700 Subject: [PATCH 03/37] first pass, not fully resolve issue with rest calls --- frontend/src/views/Game.tsx | 52 +++++++++++++++++++++--------------- frontend/src/views/Lobby.tsx | 2 +- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index d081722c4..e82d0f183 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -115,8 +115,9 @@ function GamePage() { const [players, setPlayers] = useState([]); const [gameTimer, setGameTimer] = useState(null); const [problems, setProblems] = useState([]); - const [currentLanguage, setCurrentLanguage] = useState(Language.Python); - const [currentCode, setCurrentCode] = useState(''); + const [currentLanguage] = useState([Language.Python]); + const [currentCode] = useState(['']); + const currentProblem = 0; const [timeUp, setTimeUp] = useState(false); const [allSolved, setAllSolved] = useState(false); const [defaultCodeList, setDefaultCodeList] = useState([]); @@ -147,8 +148,16 @@ function GamePage() { setTimeUp(newGame.gameTimer.timeUp); }; + const setCurrentLanguage = (newLanguage: Language) => { + currentLanguage[currentProblem] = newLanguage; + }; + + const setCurrentCode = (newCode: string) => { + currentCode[currentProblem] = newCode; + }; + const setDefaultCodeFromProblems = useCallback((problemsParam: Problem[], - code: string, language: Language) => { + code: string, language: Language[]) => { const promises: Promise[] = []; problemsParam.forEach((problem) => { if (problem && problem.problemId) { @@ -158,19 +167,20 @@ function GamePage() { // Get the result of promises and set the default code list. Promise.all(promises).then((result) => { - const codeMap = result[0]; + const codeMap = result[currentProblem]; // If previous code and language specified, save those as defaults if (code) { - codeMap[language] = code; + codeMap[language[currentProblem]] = code; } // Set this user's current code and language - setCurrentCode(codeMap[language]); - setCurrentLanguage(language); + setCurrentCode(codeMap[language[currentProblem]]); + setCurrentLanguage(language[currentProblem]); setDefaultCodeList(result); }).catch((err) => { + console.log('This error is thrown'); setError(err.message); }); }, [setDefaultCodeList, setCurrentCode, setCurrentLanguage]); @@ -250,14 +260,14 @@ function GamePage() { // If this user refreshed and has already submitted code, load and save their latest code res.players.forEach((player) => { if (player.user.userId === location.state.currentUser.userId && player.code) { - setDefaultCodeFromProblems(res.problems, player.code, player.language as Language); + setDefaultCodeFromProblems(res.problems, player.code, [player.language as Language]); matchFound = true; } }); // If no previous code, proceed as normal with the default Python language if (!matchFound) { - setDefaultCodeFromProblems(res.problems, '', Language.Python); + setDefaultCodeFromProblems(res.problems, '', [Language.Python]); } }) .catch((err) => { @@ -269,7 +279,7 @@ function GamePage() { error: errorHandler('No valid room details were provided, so you could not view the game page.'), }); } - }, [location, history, setDefaultCodeFromProblems]); + }, [location, history]); // Creates Event when splitter bar is dragged const onSecondaryPanelSizeChange = () => { @@ -311,8 +321,8 @@ function GamePage() { const request = { initiator: currentUser!, input, - code: currentCode, - language: currentLanguage, + code: currentCode[currentProblem], + language: currentLanguage[currentProblem], }; runSolution(roomId, request) @@ -336,8 +346,8 @@ function GamePage() { setError(''); const request = { initiator: currentUser!, - code: currentCode, - language: currentLanguage, + code: currentCode[currentProblem], + language: currentLanguage[currentProblem], }; submitSolution(roomId, request) @@ -415,18 +425,18 @@ function GamePage() { > {/* Problem title/description panel */} - {problems[0]?.name} - {problems[0] ? ( + {problems[currentProblem]?.name} + {problems[currentProblem] ? ( - {displayNameFromDifficulty(problems[0].difficulty!)} + {displayNameFromDifficulty(problems[currentProblem].difficulty!)} ) : null} ''} readOnly /> @@ -456,14 +466,14 @@ function GamePage() { onCodeChange={setCurrentCode} onLanguageChange={setCurrentLanguage} codeMap={defaultCodeList[0]} - defaultLanguage={currentLanguage} + defaultLanguage={currentLanguage[currentProblem]} defaultCode={null} /> { - setError('') + setError(''); setLoading(true); const prevNumProblems = numProblems; const settings = { From 73183f08ca07f87cdb4ce12cdd2af91084861a34 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Tue, 13 Apr 2021 16:48:21 -0700 Subject: [PATCH 04/37] to merge --- frontend/package-lock.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 2ad6f7a72..45a0ffae8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -3859,6 +3859,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", + "hasInstallScript": true, "optional": true, "dependencies": { "node-gyp-build": "~3.7.0" @@ -16290,6 +16291,7 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", + "hasInstallScript": true, "optional": true, "dependencies": { "node-gyp-build": "~3.7.0" From 0f14b51db6a2eab10f52d6fc4e5bf93718583bf6 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Tue, 13 Apr 2021 17:04:30 -0700 Subject: [PATCH 05/37] responding to comments I can respond to --- frontend/src/api/Game.ts | 2 +- frontend/src/views/Lobby.tsx | 17 +++++------------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/frontend/src/api/Game.ts b/frontend/src/api/Game.ts index cdd5844d3..a90f2fb13 100644 --- a/frontend/src/api/Game.ts +++ b/frontend/src/api/Game.ts @@ -115,4 +115,4 @@ export const playAgain = (roomId: string, params: PlayAgainParams): .then((res) => res.data) .catch((err) => { throw axiosErrorHandler(err); - }); \ No newline at end of file + }); diff --git a/frontend/src/views/Lobby.tsx b/frontend/src/views/Lobby.tsx index b2244c6fa..03f5976aa 100644 --- a/frontend/src/views/Lobby.tsx +++ b/frontend/src/views/Lobby.tsx @@ -143,7 +143,7 @@ function LobbyPage() { const [difficulty, setDifficulty] = useState(null); const [duration, setDuration] = useState(15); const [size, setSize] = useState(10); - const [numProblems, setNumProblems] = useState(1); + const [numProblems, setNumProblems] = useState(1); const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 }); const [hoverVisible, setHoverVisible] = useState(false); @@ -389,7 +389,7 @@ function LobbyPage() { }; const updateNumProblems = () => { - setError('') + setError(''); setLoading(true); const prevNumProblems = numProblems; const settings = { @@ -757,16 +757,9 @@ function LobbyPage() { value={numProblems} disabled={!isHost(currentUser)} onChange={(e) => { - const { value } = e.target; - - // Set numProblems to undefined to allow users to clear field - if (!value) { - setNumProblems(undefined); - } else { - const newNumProblems = Number(value); - if (newNumProblems >= 1 && newNumProblems <= 10) { - setNumProblems(newNumProblems); - } + const newNumProblems = Number(e.target); + if (newNumProblems >= 1 && newNumProblems <= 10) { + setNumProblems(newNumProblems); } }} onMouseUp={updateNumProblems} From 6d52dadfe45a5350bf8a768f15422a99ab172679 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Thu, 15 Apr 2021 15:48:48 -0700 Subject: [PATCH 06/37] extra commit to run pipeline --- frontend/src/views/Game.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index e82d0f183..51101e8a0 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -180,7 +180,6 @@ function GamePage() { setDefaultCodeList(result); }).catch((err) => { - console.log('This error is thrown'); setError(err.message); }); }, [setDefaultCodeList, setCurrentCode, setCurrentLanguage]); From b9122f997d589a60c0d251f4c31ced0c29479582 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Thu, 15 Apr 2021 17:45:17 -0700 Subject: [PATCH 07/37] Let's see if this works --- .github/workflows/workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 8ae21e09b..d8d0d81c5 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -2,7 +2,7 @@ name: Java CI/CD on: push: - branches: [ main ] + branches: [ main ], [ changeFrontEndtoTakeMultipleProblems ] pull_request: branches: [ main ] From 98e6ed86f8c2564e4a74a1821f7566f02c4cf935 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Thu, 15 Apr 2021 17:47:13 -0700 Subject: [PATCH 08/37] fix acc. yml rules --- .github/workflows/workflow.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index d8d0d81c5..46679caea 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -2,7 +2,9 @@ name: Java CI/CD on: push: - branches: [ main ], [ changeFrontEndtoTakeMultipleProblems ] + branches: + [ main ] + [ changeFrontEndtoTakeMultipleProblems ] pull_request: branches: [ main ] From 3b7e4ea077fc7cc38c5f16ee47efdca85470b3a5 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Thu, 15 Apr 2021 17:54:13 -0700 Subject: [PATCH 09/37] Please let this website be right --- .github/workflows/workflow.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 46679caea..5bab8ce9d 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -2,9 +2,7 @@ name: Java CI/CD on: push: - branches: - [ main ] - [ changeFrontEndtoTakeMultipleProblems ] + branches: [ main, changeFrontEndtoTakeMultipleProblems ] pull_request: branches: [ main ] From 1a5200beebd1fb17e5705b16312e385a785d152a Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Tue, 20 Apr 2021 20:00:17 -0700 Subject: [PATCH 10/37] add back setstate function --- frontend/src/api/Problem.ts | 1 + frontend/src/views/Game.tsx | 85 +++++++++++++++++++++++++------------ 2 files changed, 59 insertions(+), 27 deletions(-) diff --git a/frontend/src/api/Problem.ts b/frontend/src/api/Problem.ts index fcc4654ee..8a511f239 100644 --- a/frontend/src/api/Problem.ts +++ b/frontend/src/api/Problem.ts @@ -170,5 +170,6 @@ export const getDefaultCodeMap = (problemId: string): Promise = .get(routes.defaultCodeMap(problemId)) .then((res) => res.data) .catch((err) => { + console.log("this is where it's all coming from"); throw axiosErrorHandler(err); }); diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index 51101e8a0..327fcb6fe 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -30,7 +30,7 @@ import { import LeaderboardCard from '../components/card/LeaderboardCard'; import GameTimerContainer from '../components/game/GameTimerContainer'; import { GameTimer } from '../api/GameTimer'; -import { TextButton, DifficultyDisplayButton } from '../components/core/Button'; +import { TextButton, DifficultyDisplayButton, SmallButton } from '../components/core/Button'; import { connect, routes, send, subscribe, } from '../api/Socket'; @@ -115,9 +115,9 @@ function GamePage() { const [players, setPlayers] = useState([]); const [gameTimer, setGameTimer] = useState(null); const [problems, setProblems] = useState([]); - const [currentLanguage] = useState([Language.Python]); - const [currentCode] = useState(['']); - const currentProblem = 0; + const [currentLanguage, setCurrentLanguage] = useState([Language.Python]); + const [currentCode, setCurrentCode] = useState(['']); + const [currentProblem, setCurrentProblem] = useState(0); const [timeUp, setTimeUp] = useState(false); const [allSolved, setAllSolved] = useState(false); const [defaultCodeList, setDefaultCodeList] = useState([]); @@ -148,12 +148,24 @@ function GamePage() { setTimeUp(newGame.gameTimer.timeUp); }; - const setCurrentLanguage = (newLanguage: Language) => { - currentLanguage[currentProblem] = newLanguage; + const createCodeLanguageArray = () => { + while (currentLanguage.length < problems.length) { + currentLanguage.push(Language.Python); + } + + while (currentCode.length < problems.length) { + currentCode.push(''); + } + }; + + createCodeLanguageArray(); + + const setOneCurrentLanguage = (newLanguage: Language) => { + currentLanguage[currentProblem.valueOf()] = newLanguage; }; - const setCurrentCode = (newCode: string) => { - currentCode[currentProblem] = newCode; + const setOneCurrentCode = (newCode: string) => { + currentCode[currentProblem.valueOf()] = newCode; }; const setDefaultCodeFromProblems = useCallback((problemsParam: Problem[], @@ -167,22 +179,22 @@ function GamePage() { // Get the result of promises and set the default code list. Promise.all(promises).then((result) => { - const codeMap = result[currentProblem]; + const codeMap = result[0]; // If previous code and language specified, save those as defaults if (code) { - codeMap[language[currentProblem]] = code; + codeMap[language[0]] = code; } // Set this user's current code and language - setCurrentCode(codeMap[language[currentProblem]]); - setCurrentLanguage(language[currentProblem]); + setCurrentCode([codeMap[language[0]]]); + setCurrentLanguage(language); setDefaultCodeList(result); }).catch((err) => { setError(err.message); }); - }, [setDefaultCodeList, setCurrentCode, setCurrentLanguage]); + }, [setDefaultCodeList, setCurrentCode, setCurrentLanguage, currentProblem]); /** * Display the notification as a callback from the notification @@ -278,7 +290,7 @@ function GamePage() { error: errorHandler('No valid room details were provided, so you could not view the game page.'), }); } - }, [location, history]); + }, [location, history, setDefaultCodeFromProblems]); // Creates Event when splitter bar is dragged const onSecondaryPanelSizeChange = () => { @@ -320,8 +332,8 @@ function GamePage() { const request = { initiator: currentUser!, input, - code: currentCode[currentProblem], - language: currentLanguage[currentProblem], + code: currentCode[currentProblem.valueOf()], + language: currentLanguage[currentProblem.valueOf()], }; runSolution(roomId, request) @@ -345,8 +357,8 @@ function GamePage() { setError(''); const request = { initiator: currentUser!, - code: currentCode[currentProblem], - language: currentLanguage[currentProblem], + code: currentCode[currentProblem.valueOf()], + language: currentLanguage[currentProblem.valueOf()], }; submitSolution(roomId, request) @@ -364,6 +376,20 @@ function GamePage() { }); }; + const nextProblem = () => { + setCurrentProblem((currentProblem.valueOf() + 1) % problems?.length); + }; + + const previousProblem = () => { + let temp = currentProblem.valueOf() - 1; + + if (temp < 0) { + temp += problems?.length; + } + + setCurrentProblem(temp); + }; + const displayPlayerLeaderboard = useCallback(() => players.map((player, index) => ( {/* Problem title/description panel */} - {problems[currentProblem]?.name} - {problems[currentProblem] ? ( + {problems[currentProblem.valueOf()]?.name} + {problems[currentProblem.valueOf()] ? ( - {displayNameFromDifficulty(problems[currentProblem].difficulty!)} + {displayNameFromDifficulty(problems[currentProblem.valueOf()].difficulty!)} ) : null} ''} readOnly /> @@ -462,17 +489,17 @@ function GamePage() { > + + Previous + Next + setCopiedEmail(false)}> Email copied!  ✕ From 4e499fc67eb154d3b5ee3d0d9da64154d83b4b9b Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Tue, 20 Apr 2021 20:19:06 -0700 Subject: [PATCH 11/37] changing indexes so that dependency doesn't have to be added --- frontend/src/views/Game.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index 327fcb6fe..f29f39801 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -194,7 +194,7 @@ function GamePage() { }).catch((err) => { setError(err.message); }); - }, [setDefaultCodeList, setCurrentCode, setCurrentLanguage, currentProblem]); + }, [setDefaultCodeList, setCurrentCode, setCurrentLanguage]); /** * Display the notification as a callback from the notification From 4817790eee4001fc547c2c4134a9b41f934c4741 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Wed, 21 Apr 2021 19:16:24 -0700 Subject: [PATCH 12/37] First pass fixing codeEditor --- frontend/src/components/game/Editor.tsx | 19 +++++++++++++------ frontend/src/views/Game.tsx | 15 ++++++++++----- frontend/src/views/Results.tsx | 1 + 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/game/Editor.tsx b/frontend/src/components/game/Editor.tsx index b88779ef0..47d53c551 100644 --- a/frontend/src/components/game/Editor.tsx +++ b/frontend/src/components/game/Editor.tsx @@ -8,9 +8,10 @@ import { DefaultCodeType } from '../../api/Problem'; type EditorProps = { onLanguageChange: ((language: Language) => void) | null, onCodeChange: ((code: string) => void) | null, - codeMap: DefaultCodeType | null, + codeMap: DefaultCodeType[] | null, defaultLanguage: Language, defaultCode: string | null, + currentProblem: number }; const Content = styled.div` @@ -88,7 +89,7 @@ const monacoEditorOptions: EditorConstructionOptions = { // This function refreshes the width of Monaco editor upon change in container size function ResizableMonacoEditor(props: EditorProps) { const { - onLanguageChange, onCodeChange, codeMap, defaultLanguage, defaultCode, + onLanguageChange, onCodeChange, codeMap, defaultLanguage, defaultCode, currentProblem, } = props; const theme = useContext(ThemeContext); @@ -124,8 +125,8 @@ function ResizableMonacoEditor(props: EditorProps) { const handleLanguageChange = (language: Language) => { // Save the code for this language if (codeMap != null && codeEditor != null) { - codeMap[currentLanguage] = codeEditor.getValue(); - codeEditor.setValue(codeMap[language]); + codeMap[currentProblem][currentLanguage] = codeEditor.getValue(); + codeEditor.setValue(codeMap[currentProblem][language]); } // Change the language and initial code for the editor @@ -136,10 +137,16 @@ function ResizableMonacoEditor(props: EditorProps) { }; useEffect(() => { + console.log(codeMap); + console.log(currentProblem); + console.log(currentLanguage); + if (codeMap != null && codeEditor != null) { - codeEditor.setValue(codeMap[currentLanguage]); + if (codeMap[currentProblem] != null) { + codeEditor.setValue(codeMap[currentProblem][currentLanguage]); + } } - }, [currentLanguage, codeMap, codeEditor, setCodeEditor]); + }, [currentLanguage, codeMap, codeEditor, setCodeEditor, currentProblem]); return ( diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index f29f39801..6a3c2e7d2 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -179,22 +179,26 @@ function GamePage() { // Get the result of promises and set the default code list. Promise.all(promises).then((result) => { - const codeMap = result[0]; + const codeList = []; + + for (let i = 0; i < result.length; i += 1) { + codeList.push(result[i][language[i]]); + } // If previous code and language specified, save those as defaults if (code) { - codeMap[language[0]] = code; + codeList[currentProblem.valueOf()] = code; } // Set this user's current code and language - setCurrentCode([codeMap[language[0]]]); + setCurrentCode(codeList); setCurrentLanguage(language); setDefaultCodeList(result); }).catch((err) => { setError(err.message); }); - }, [setDefaultCodeList, setCurrentCode, setCurrentLanguage]); + }, [setDefaultCodeList, setCurrentCode, setCurrentLanguage, currentProblem]); /** * Display the notification as a callback from the notification @@ -491,7 +495,8 @@ function GamePage() { diff --git a/frontend/src/views/Results.tsx b/frontend/src/views/Results.tsx index c85c27f46..bcb10caa1 100644 --- a/frontend/src/views/Results.tsx +++ b/frontend/src/views/Results.tsx @@ -274,6 +274,7 @@ function GameResultsPage() { onLanguageChange={null} onCodeChange={null} codeMap={null} + currentProblem={0} defaultLanguage={bestSubmission?.language as Language || Language.Python} defaultCode={bestSubmission?.code || 'Uh oh! An error occurred fetching this player\'s code'} /> From c1a9cbddb30c001c19b667262eeac3952c1d7bd8 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Thu, 22 Apr 2021 15:31:36 -0700 Subject: [PATCH 13/37] Catching up --- frontend/src/components/game/Editor.tsx | 19 ++++++++++++------- frontend/src/views/Game.tsx | 7 +++++-- frontend/src/views/Results.tsx | 3 ++- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/game/Editor.tsx b/frontend/src/components/game/Editor.tsx index 47d53c551..4cf4133f3 100644 --- a/frontend/src/components/game/Editor.tsx +++ b/frontend/src/components/game/Editor.tsx @@ -8,10 +8,11 @@ import { DefaultCodeType } from '../../api/Problem'; type EditorProps = { onLanguageChange: ((language: Language) => void) | null, onCodeChange: ((code: string) => void) | null, - codeMap: DefaultCodeType[] | null, + defaultCodeMap: DefaultCodeType[] | null, defaultLanguage: Language, defaultCode: string | null, - currentProblem: number + currentProblem: number, + newLanguage: Language }; const Content = styled.div` @@ -89,17 +90,23 @@ const monacoEditorOptions: EditorConstructionOptions = { // This function refreshes the width of Monaco editor upon change in container size function ResizableMonacoEditor(props: EditorProps) { const { - onLanguageChange, onCodeChange, codeMap, defaultLanguage, defaultCode, currentProblem, + onLanguageChange, onCodeChange, defaultCodeMap, defaultLanguage, defaultCode, currentProblem, + newLanguage, } = props; const theme = useContext(ThemeContext); const [currentLanguage, setCurrentLanguage] = useState(defaultLanguage); const [codeEditor, setCodeEditor] = useState(null); + const [codeMap, setCodeMap] = useState(defaultCodeMap); useEffect(() => { setCurrentLanguage(defaultLanguage); }, [defaultLanguage]); + useEffect(() => { + setCodeMap(defaultCodeMap); + }, [defaultCodeMap]); + const handleEditorDidMount = (editor: monaco.editor.IStandaloneCodeEditor) => { setCodeEditor(editor); window.addEventListener('resize', () => { @@ -125,7 +132,9 @@ function ResizableMonacoEditor(props: EditorProps) { const handleLanguageChange = (language: Language) => { // Save the code for this language if (codeMap != null && codeEditor != null) { + console.log(codeMap[currentProblem][currentLanguage]); codeMap[currentProblem][currentLanguage] = codeEditor.getValue(); + console.log(codeMap[currentProblem][currentLanguage]); codeEditor.setValue(codeMap[currentProblem][language]); } @@ -137,10 +146,6 @@ function ResizableMonacoEditor(props: EditorProps) { }; useEffect(() => { - console.log(codeMap); - console.log(currentProblem); - console.log(currentLanguage); - if (codeMap != null && codeEditor != null) { if (codeMap[currentProblem] != null) { codeEditor.setValue(codeMap[currentProblem][currentLanguage]); diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index 6a3c2e7d2..0ee18df39 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -331,6 +331,8 @@ function GamePage() { // Callback when user runs code against custom test case const runCode = (input: string) => { + console.log(problems[currentProblem.valueOf()].name); + setLoading(true); setError(''); const request = { @@ -495,9 +497,10 @@ function GamePage() { diff --git a/frontend/src/views/Results.tsx b/frontend/src/views/Results.tsx index bcb10caa1..99478c914 100644 --- a/frontend/src/views/Results.tsx +++ b/frontend/src/views/Results.tsx @@ -273,9 +273,10 @@ function GameResultsPage() { From 79d3401a6d532e1542aee4280c726550c48954b9 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Sat, 24 Apr 2021 16:29:41 -0700 Subject: [PATCH 14/37] responding to some of Chris's comments --- .github/workflows/workflow.yml | 2 +- frontend/src/api/Problem.ts | 1 - frontend/src/components/game/Editor.tsx | 4 +--- frontend/src/views/Game.tsx | 1 - frontend/src/views/Results.tsx | 1 - 5 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 5bab8ce9d..5c3867e97 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -2,7 +2,7 @@ name: Java CI/CD on: push: - branches: [ main, changeFrontEndtoTakeMultipleProblems ] + branches: [ main, stagingNumProblems ] pull_request: branches: [ main ] diff --git a/frontend/src/api/Problem.ts b/frontend/src/api/Problem.ts index 8a511f239..fcc4654ee 100644 --- a/frontend/src/api/Problem.ts +++ b/frontend/src/api/Problem.ts @@ -170,6 +170,5 @@ export const getDefaultCodeMap = (problemId: string): Promise = .get(routes.defaultCodeMap(problemId)) .then((res) => res.data) .catch((err) => { - console.log("this is where it's all coming from"); throw axiosErrorHandler(err); }); diff --git a/frontend/src/components/game/Editor.tsx b/frontend/src/components/game/Editor.tsx index 4cf4133f3..7cc2c9cae 100644 --- a/frontend/src/components/game/Editor.tsx +++ b/frontend/src/components/game/Editor.tsx @@ -11,8 +11,7 @@ type EditorProps = { defaultCodeMap: DefaultCodeType[] | null, defaultLanguage: Language, defaultCode: string | null, - currentProblem: number, - newLanguage: Language + currentProblem: number }; const Content = styled.div` @@ -91,7 +90,6 @@ const monacoEditorOptions: EditorConstructionOptions = { function ResizableMonacoEditor(props: EditorProps) { const { onLanguageChange, onCodeChange, defaultCodeMap, defaultLanguage, defaultCode, currentProblem, - newLanguage, } = props; const theme = useContext(ThemeContext); diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index 0ee18df39..cfa2a1fe8 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -500,7 +500,6 @@ function GamePage() { defaultCodeMap={defaultCodeList} currentProblem={currentProblem.valueOf()} defaultLanguage={Language.Python} - newLanguage={currentLanguage[currentProblem.valueOf()]} defaultCode={null} /> diff --git a/frontend/src/views/Results.tsx b/frontend/src/views/Results.tsx index 99478c914..6abe6290d 100644 --- a/frontend/src/views/Results.tsx +++ b/frontend/src/views/Results.tsx @@ -276,7 +276,6 @@ function GameResultsPage() { defaultCodeMap={null} currentProblem={0} defaultLanguage={bestSubmission?.language as Language || Language.Python} - newLanguage={Language.Python} defaultCode={bestSubmission?.code || 'Uh oh! An error occurred fetching this player\'s code'} /> From 1c622c0e08391005e28046a617f610aa0c6bcf3a Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Sat, 24 Apr 2021 17:48:31 -0700 Subject: [PATCH 15/37] using setCodeMap --- frontend/src/components/game/Editor.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/game/Editor.tsx b/frontend/src/components/game/Editor.tsx index 7cc2c9cae..70ddc0007 100644 --- a/frontend/src/components/game/Editor.tsx +++ b/frontend/src/components/game/Editor.tsx @@ -131,7 +131,9 @@ function ResizableMonacoEditor(props: EditorProps) { // Save the code for this language if (codeMap != null && codeEditor != null) { console.log(codeMap[currentProblem][currentLanguage]); - codeMap[currentProblem][currentLanguage] = codeEditor.getValue(); + let codeMapTemp = codeMap; + codeMapTemp[currentProblem][currentLanguage] = codeEditor.getValue(); + setCodeMap(codeMapTemp); console.log(codeMap[currentProblem][currentLanguage]); codeEditor.setValue(codeMap[currentProblem][language]); } From 382ed9bedb4946e37af5c7eaba97dbfc41393628 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Mon, 26 Apr 2021 20:08:29 -0700 Subject: [PATCH 16/37] fixing bugs --- frontend/package-lock.json | 2 -- frontend/src/components/game/Editor.tsx | 33 ++++++++++++++++++++----- frontend/src/views/Game.tsx | 3 ++- frontend/src/views/Lobby.tsx | 2 +- frontend/src/views/Results.tsx | 1 + 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 45a0ffae8..2ad6f7a72 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -3859,7 +3859,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "hasInstallScript": true, "optional": true, "dependencies": { "node-gyp-build": "~3.7.0" @@ -16291,7 +16290,6 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", - "hasInstallScript": true, "optional": true, "dependencies": { "node-gyp-build": "~3.7.0" diff --git a/frontend/src/components/game/Editor.tsx b/frontend/src/components/game/Editor.tsx index 70ddc0007..c58e79dae 100644 --- a/frontend/src/components/game/Editor.tsx +++ b/frontend/src/components/game/Editor.tsx @@ -8,6 +8,7 @@ import { DefaultCodeType } from '../../api/Problem'; type EditorProps = { onLanguageChange: ((language: Language) => void) | null, onCodeChange: ((code: string) => void) | null, + getCurrentLanguage: (() => Language) | null, defaultCodeMap: DefaultCodeType[] | null, defaultLanguage: Language, defaultCode: string | null, @@ -89,13 +90,14 @@ const monacoEditorOptions: EditorConstructionOptions = { // This function refreshes the width of Monaco editor upon change in container size function ResizableMonacoEditor(props: EditorProps) { const { - onLanguageChange, onCodeChange, defaultCodeMap, defaultLanguage, defaultCode, currentProblem, + onLanguageChange, onCodeChange, getCurrentLanguage, defaultCodeMap, defaultLanguage, defaultCode, currentProblem, } = props; const theme = useContext(ThemeContext); const [currentLanguage, setCurrentLanguage] = useState(defaultLanguage); const [codeEditor, setCodeEditor] = useState(null); const [codeMap, setCodeMap] = useState(defaultCodeMap); + const [previousProblem, setPreviousProblem] = useState(0); useEffect(() => { setCurrentLanguage(defaultLanguage); @@ -130,11 +132,9 @@ function ResizableMonacoEditor(props: EditorProps) { const handleLanguageChange = (language: Language) => { // Save the code for this language if (codeMap != null && codeEditor != null) { - console.log(codeMap[currentProblem][currentLanguage]); - let codeMapTemp = codeMap; + const codeMapTemp = codeMap; codeMapTemp[currentProblem][currentLanguage] = codeEditor.getValue(); setCodeMap(codeMapTemp); - console.log(codeMap[currentProblem][currentLanguage]); codeEditor.setValue(codeMap[currentProblem][language]); } @@ -146,12 +146,33 @@ function ResizableMonacoEditor(props: EditorProps) { }; useEffect(() => { + console.log('again'); + if (codeMap != null && codeEditor != null) { if (codeMap[currentProblem] != null) { - codeEditor.setValue(codeMap[currentProblem][currentLanguage]); + const codeMapTemp = codeMap; + + if (codeEditor.getValue() !== 'Loading...') { + codeMapTemp[previousProblem][currentLanguage] = codeEditor.getValue(); + } + + setCodeMap(codeMapTemp); + setPreviousProblem(currentProblem); + + let newLanguage = currentLanguage; + + if (getCurrentLanguage !== null) { + newLanguage = getCurrentLanguage(); + + if (newLanguage !== currentLanguage) { + setCurrentLanguage(newLanguage); + } + } + + codeEditor.setValue(codeMap[currentProblem][newLanguage]); } } - }, [currentLanguage, codeMap, codeEditor, setCodeEditor, currentProblem]); + }, [currentLanguage, codeMap, codeEditor, setCodeEditor, currentProblem, previousProblem]); return ( diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index a05a52e8e..280b6a15c 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -199,7 +199,7 @@ function GamePage() { }).catch((err) => { setError(err.message); }); - }, [setDefaultCodeList, setCurrentCode, setCurrentLanguage, currentProblem]); + }, [setDefaultCodeList, setCurrentCode, setCurrentLanguage]); /** * Display the notification as a callback from the notification @@ -498,6 +498,7 @@ function GamePage() { currentLanguage[currentProblem.valueOf()]} defaultCodeMap={defaultCodeList} currentProblem={currentProblem.valueOf()} defaultLanguage={Language.Python} diff --git a/frontend/src/views/Lobby.tsx b/frontend/src/views/Lobby.tsx index 316d7d917..9881964cf 100644 --- a/frontend/src/views/Lobby.tsx +++ b/frontend/src/views/Lobby.tsx @@ -788,7 +788,7 @@ function LobbyPage() { value={numProblems} disabled={!isHost(currentUser)} onChange={(e) => { - const newNumProblems = Number(e.target); + const newNumProblems = Number(e.target.value); if (newNumProblems >= 1 && newNumProblems <= 10) { setNumProblems(newNumProblems); } diff --git a/frontend/src/views/Results.tsx b/frontend/src/views/Results.tsx index 6f5cea5dd..91e1e3b6d 100644 --- a/frontend/src/views/Results.tsx +++ b/frontend/src/views/Results.tsx @@ -273,6 +273,7 @@ function GameResultsPage() { Date: Wed, 28 Apr 2021 16:31:38 -0700 Subject: [PATCH 17/37] Fixing final bugs --- frontend/src/components/core/Button.tsx | 2 +- frontend/src/components/game/Editor.tsx | 2 +- frontend/src/views/Game.tsx | 4 ++++ .../java/com/codejoust/main/dto/game/SubmissionRequest.java | 1 + src/main/java/com/codejoust/main/service/SubmitService.java | 4 ++-- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/core/Button.tsx b/frontend/src/components/core/Button.tsx index 1166d46b0..fe83350e2 100644 --- a/frontend/src/components/core/Button.tsx +++ b/frontend/src/components/core/Button.tsx @@ -171,7 +171,7 @@ export const SmallButton = styled(DefaultButton)` color: ${({ theme }) => theme.colors.white}; background: ${({ theme }) => theme.colors.gradients.blue}; font-size: ${({ theme }) => theme.fontSize.medium}; - padding: 0; + padding: 5px 20px; margin: 0.4rem; width: 6rem; height: 2.25rem; diff --git a/frontend/src/components/game/Editor.tsx b/frontend/src/components/game/Editor.tsx index c58e79dae..921a0e5b8 100644 --- a/frontend/src/components/game/Editor.tsx +++ b/frontend/src/components/game/Editor.tsx @@ -172,7 +172,7 @@ function ResizableMonacoEditor(props: EditorProps) { codeEditor.setValue(codeMap[currentProblem][newLanguage]); } } - }, [currentLanguage, codeMap, codeEditor, setCodeEditor, currentProblem, previousProblem]); + }, [currentLanguage, codeMap, codeEditor, setCodeEditor, currentProblem, previousProblem, getCurrentLanguage]); return ( diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index 280b6a15c..4f79aa746 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -341,6 +341,7 @@ function GamePage() { input, code: currentCode[currentProblem.valueOf()], language: currentLanguage[currentProblem.valueOf()], + problem: currentProblem, }; runSolution(roomId, request) @@ -366,6 +367,7 @@ function GamePage() { initiator: currentUser!, code: currentCode[currentProblem.valueOf()], language: currentLanguage[currentProblem.valueOf()], + problem: currentProblem, }; submitSolution(roomId, request) @@ -385,6 +387,7 @@ function GamePage() { const nextProblem = () => { setCurrentProblem((currentProblem.valueOf() + 1) % problems?.length); + setSubmission(null); }; const previousProblem = () => { @@ -395,6 +398,7 @@ function GamePage() { } setCurrentProblem(temp); + setSubmission(null); }; const displayPlayerLeaderboard = useCallback(() => players.map((player, index) => ( diff --git a/src/main/java/com/codejoust/main/dto/game/SubmissionRequest.java b/src/main/java/com/codejoust/main/dto/game/SubmissionRequest.java index d480422c7..535ce8445 100644 --- a/src/main/java/com/codejoust/main/dto/game/SubmissionRequest.java +++ b/src/main/java/com/codejoust/main/dto/game/SubmissionRequest.java @@ -13,4 +13,5 @@ public class SubmissionRequest { private String code; private String input; private UserDto initiator; + private int problem = 0; } diff --git a/src/main/java/com/codejoust/main/service/SubmitService.java b/src/main/java/com/codejoust/main/service/SubmitService.java index e8f7f2ad4..d9ce2a397 100644 --- a/src/main/java/com/codejoust/main/service/SubmitService.java +++ b/src/main/java/com/codejoust/main/service/SubmitService.java @@ -106,7 +106,7 @@ public SubmissionDto runCode(Game game, SubmissionRequest request) { testerRequest.setLanguage(request.getLanguage()); // Set the problem with the single provided test case. - ProblemDto problemDto = getStrippedProblemDto(game.getProblems().get(0)); + ProblemDto problemDto = getStrippedProblemDto(game.getProblems().get(request.getProblem())); /** * Provide a temporary output to circumvent output parsing error. @@ -146,7 +146,7 @@ public SubmissionDto submitSolution(Game game, SubmissionRequest request) { testerRequest.setLanguage(request.getLanguage()); // Invariant: Games have at least one problem (else it will fail to create) - ProblemDto problemDto = getStrippedProblemDto(game.getProblems().get(0)); + ProblemDto problemDto = getStrippedProblemDto(game.getProblems().get(request.getProblem())); testerRequest.setProblem(problemDto); Submission submission = getSubmission(testerRequest); From 202c52e167b88aa492c2710d960488c55e688c5d Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Wed, 28 Apr 2021 18:18:09 -0700 Subject: [PATCH 18/37] minute changes that I made while writing the PR --- frontend/src/components/core/Button.tsx | 2 +- frontend/src/components/game/Editor.tsx | 2 -- frontend/src/views/Game.tsx | 4 +--- .../com/codejoust/main/service/GameManagementService.java | 8 ++++++++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/core/Button.tsx b/frontend/src/components/core/Button.tsx index fe83350e2..1166d46b0 100644 --- a/frontend/src/components/core/Button.tsx +++ b/frontend/src/components/core/Button.tsx @@ -171,7 +171,7 @@ export const SmallButton = styled(DefaultButton)` color: ${({ theme }) => theme.colors.white}; background: ${({ theme }) => theme.colors.gradients.blue}; font-size: ${({ theme }) => theme.fontSize.medium}; - padding: 5px 20px; + padding: 0; margin: 0.4rem; width: 6rem; height: 2.25rem; diff --git a/frontend/src/components/game/Editor.tsx b/frontend/src/components/game/Editor.tsx index 921a0e5b8..f706c4722 100644 --- a/frontend/src/components/game/Editor.tsx +++ b/frontend/src/components/game/Editor.tsx @@ -146,8 +146,6 @@ function ResizableMonacoEditor(props: EditorProps) { }; useEffect(() => { - console.log('again'); - if (codeMap != null && codeEditor != null) { if (codeMap[currentProblem] != null) { const codeMapTemp = codeMap; diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index 4f79aa746..72e41260c 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -188,7 +188,7 @@ function GamePage() { // If previous code and language specified, save those as defaults if (code) { - codeList[currentProblem.valueOf()] = code; + codeList[0] = code; } // Set this user's current code and language @@ -332,8 +332,6 @@ function GamePage() { // Callback when user runs code against custom test case const runCode = (input: string) => { - console.log(problems[currentProblem.valueOf()].name); - setLoading(true); setError(''); const request = { diff --git a/src/main/java/com/codejoust/main/service/GameManagementService.java b/src/main/java/com/codejoust/main/service/GameManagementService.java index 73532a49e..d86527187 100644 --- a/src/main/java/com/codejoust/main/service/GameManagementService.java +++ b/src/main/java/com/codejoust/main/service/GameManagementService.java @@ -185,6 +185,10 @@ public SubmissionDto runCode(String roomId, SubmissionRequest request) { throw new ApiException(GameError.EMPTY_FIELD); } + if (request.getProblem() >= game.getProblems().size() || request.getProblem() < 0) { + throw new ApiException(GameError.BAD_SETTING); + } + String initiatorUserId = request.getInitiator().getUserId(); if (!game.getPlayers().containsKey(initiatorUserId)) { throw new ApiException(GameError.INVALID_PERMISSIONS); @@ -201,6 +205,10 @@ public SubmissionDto submitSolution(String roomId, SubmissionRequest request) { throw new ApiException(GameError.EMPTY_FIELD); } + if (request.getProblem() >= game.getProblems().size() || request.getProblem() < 0) { + throw new ApiException(GameError.BAD_SETTING); + } + String initiatorUserId = request.getInitiator().getUserId(); if (!game.getPlayers().containsKey(initiatorUserId)) { throw new ApiException(GameError.INVALID_PERMISSIONS); From 620ebd06645fab3c7926f43a762d63f9d208bd38 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Sat, 8 May 2021 18:42:28 -0700 Subject: [PATCH 19/37] Change so that submissions are used during reload page --- frontend/src/api/Game.ts | 5 +- frontend/src/views/Game.tsx | 110 +++++++++++------- .../main/dto/game/SubmissionDto.java | 1 + .../main/dto/game/SubmissionRequest.java | 2 +- .../main/game_object/Submission.java | 1 + .../main/service/GameManagementService.java | 4 +- .../codejoust/main/service/SubmitService.java | 11 +- .../com/codejoust/main/api/GameTests.java | 2 +- .../main/mapper/GameMapperTests.java | 2 - 9 files changed, 79 insertions(+), 59 deletions(-) diff --git a/frontend/src/api/Game.ts b/frontend/src/api/Game.ts index a90f2fb13..1b5b3291e 100644 --- a/frontend/src/api/Game.ts +++ b/frontend/src/api/Game.ts @@ -8,8 +8,6 @@ import { Color } from './Color'; export type Player = { user: User, - code: string, - language: string, submissions: Submission[], solved: boolean, color: Color, @@ -36,12 +34,14 @@ export type RunSolutionParams = { initiator: User, input: string, code: string, + problemIndex: number, language: string, }; export type SubmitSolutionParams = { initiator: User, code: string, + problemIndex: number, language: string, }; @@ -63,6 +63,7 @@ export enum SubmissionType { export type Submission = { code: string, + problemIndex: number, language: string, results: SubmissionResult[], numCorrect: number, diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index 72e41260c..f557e73bb 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -103,7 +103,7 @@ function GamePage() { const location = useLocation(); const [copiedEmail, setCopiedEmail] = useState(false); - const [submission, setSubmission] = useState(null); + const [submissions, setSubmissions] = useState([]); const [currentUser, setCurrentUser] = useState(null); const [roomId, setRoomId] = useState(''); @@ -116,9 +116,10 @@ function GamePage() { const [players, setPlayers] = useState([]); const [gameTimer, setGameTimer] = useState(null); const [problems, setProblems] = useState([]); - const [currentLanguage, setCurrentLanguage] = useState([Language.Python]); - const [currentCode, setCurrentCode] = useState(['']); - const [currentProblem, setCurrentProblem] = useState(0); + const [languageList] = useState([Language.Python]); + const [codeList, setCodeList] = useState(['']); + const [currentSubmission, setCurrentSubmission] = useState(null); + const [currentProblem, setCurrentProblem] = useState(0); const [timeUp, setTimeUp] = useState(false); const [allSolved, setAllSolved] = useState(false); const [defaultCodeList, setDefaultCodeList] = useState([]); @@ -150,27 +151,38 @@ function GamePage() { }; const createCodeLanguageArray = () => { - while (currentLanguage.length < problems.length) { - currentLanguage.push(Language.Python); + while (languageList.length < problems.length) { + languageList.push(Language.Python); } - while (currentCode.length < problems.length) { - currentCode.push(''); + while (codeList.length < problems.length) { + codeList.push(''); } }; createCodeLanguageArray(); const setOneCurrentLanguage = (newLanguage: Language) => { - currentLanguage[currentProblem.valueOf()] = newLanguage; + languageList[currentProblem] = newLanguage; }; const setOneCurrentCode = (newCode: string) => { - currentCode[currentProblem.valueOf()] = newCode; + codeList[currentProblem] = newCode; + }; + + const getSubmission = (curr: number, playerSubmissions: Submission[]) => { + for (let i = playerSubmissions.length - 1; i >= 0; i -= 1) { + if (playerSubmissions[i].problemIndex === curr) { + return playerSubmissions[i]; + } + } + + return null; }; const setDefaultCodeFromProblems = useCallback((problemsParam: Problem[], - code: string, language: Language[]) => { + playerSubmissions: Submission[]) => { + setSubmissions(playerSubmissions); const promises: Promise[] = []; problemsParam.forEach((problem) => { if (problem && problem.problemId) { @@ -180,26 +192,32 @@ function GamePage() { // Get the result of promises and set the default code list. Promise.all(promises).then((result) => { - const codeList = []; + const newCodeList = []; + const codeMap = result; for (let i = 0; i < result.length; i += 1) { - codeList.push(result[i][language[i]]); + newCodeList.push(result[i][Language.Python]); } // If previous code and language specified, save those as defaults - if (code) { - codeList[0] = code; + for (let i = 0; i < result.length; i += 1) { + const temp = getSubmission(i, playerSubmissions); + + if (temp != null) { + newCodeList[i] = temp.code; + codeMap[i][temp.language as Language] = temp.code; + languageList[i] = temp.language as Language; + setCurrentProblem(i); + } } - // Set this user's current code and language - setCurrentCode(codeList); - setCurrentLanguage(language); - - setDefaultCodeList(result); + // Set this user's current code + setCodeList(newCodeList); + setDefaultCodeList(codeMap); }).catch((err) => { setError(err.message); }); - }, [setDefaultCodeList, setCurrentCode, setCurrentLanguage]); + }, [setDefaultCodeList, setCodeList, languageList]); /** * Display the notification as a callback from the notification @@ -275,15 +293,15 @@ function GamePage() { // If this user refreshed and has already submitted code, load and save their latest code res.players.forEach((player) => { - if (player.user.userId === location.state.currentUser.userId && player.code) { - setDefaultCodeFromProblems(res.problems, player.code, [player.language as Language]); + if (player.user.userId === location.state.currentUser.userId) { + setDefaultCodeFromProblems(res.problems, player.submissions); matchFound = true; } }); // If no previous code, proceed as normal with the default Python language if (!matchFound) { - setDefaultCodeFromProblems(res.problems, '', [Language.Python]); + setDefaultCodeFromProblems(res.problems, []); } }) .catch((err) => { @@ -337,9 +355,9 @@ function GamePage() { const request = { initiator: currentUser!, input, - code: currentCode[currentProblem.valueOf()], - language: currentLanguage[currentProblem.valueOf()], - problem: currentProblem, + code: codeList[currentProblem], + language: languageList[currentProblem], + problemIndex: currentProblem, }; runSolution(roomId, request) @@ -348,7 +366,8 @@ function GamePage() { // Set the 'test' submission type to correctly display result. res.submissionType = SubmissionType.Test; - setSubmission(res); + submissions.push(res); + setCurrentSubmission(getSubmission(currentProblem, submissions)); checkSendTestCorrectNotification(res); }) .catch((err) => { @@ -363,9 +382,9 @@ function GamePage() { setError(''); const request = { initiator: currentUser!, - code: currentCode[currentProblem.valueOf()], - language: currentLanguage[currentProblem.valueOf()], - problem: currentProblem, + code: codeList[currentProblem], + language: languageList[currentProblem], + problemIndex: currentProblem, }; submitSolution(roomId, request) @@ -374,7 +393,8 @@ function GamePage() { // Set the 'submit' submission type to correctly display result. res.submissionType = SubmissionType.Submit; - setSubmission(res); + submissions.push(res); + setCurrentSubmission(getSubmission(currentProblem, submissions)); checkSendSolutionCorrectNotification(res); }) .catch((err) => { @@ -384,19 +404,19 @@ function GamePage() { }; const nextProblem = () => { - setCurrentProblem((currentProblem.valueOf() + 1) % problems?.length); - setSubmission(null); + setCurrentProblem((currentProblem + 1) % problems?.length); + setCurrentSubmission(getSubmission(currentProblem, submissions)); }; const previousProblem = () => { - let temp = currentProblem.valueOf() - 1; + let temp = currentProblem - 1; if (temp < 0) { temp += problems?.length; } setCurrentProblem(temp); - setSubmission(null); + setCurrentSubmission(getSubmission(currentProblem, submissions)); }; const displayPlayerLeaderboard = useCallback(() => players.map((player, index) => ( @@ -459,19 +479,19 @@ function GamePage() { > {/* Problem title/description panel */} - {problems[currentProblem.valueOf()]?.name} - {problems[currentProblem.valueOf()] ? ( + {problems[currentProblem]?.name} + {problems[currentProblem] ? ( - {displayNameFromDifficulty(problems[currentProblem.valueOf()].difficulty!)} + {displayNameFromDifficulty(problems[currentProblem].difficulty!)} ) : null} ''} readOnly /> @@ -500,9 +520,9 @@ function GamePage() { currentLanguage[currentProblem.valueOf()]} + getCurrentLanguage={() => languageList[currentProblem]} defaultCodeMap={defaultCodeList} - currentProblem={currentProblem.valueOf()} + currentProblem={currentProblem} defaultLanguage={Language.Python} defaultCode={null} /> @@ -510,8 +530,8 @@ function GamePage() { diff --git a/src/main/java/com/codejoust/main/dto/game/SubmissionDto.java b/src/main/java/com/codejoust/main/dto/game/SubmissionDto.java index deae12dee..435949e4e 100644 --- a/src/main/java/com/codejoust/main/dto/game/SubmissionDto.java +++ b/src/main/java/com/codejoust/main/dto/game/SubmissionDto.java @@ -19,6 +19,7 @@ public class SubmissionDto { private CodeLanguage language; private String code; + private int problemIndex; private List results; private Integer numCorrect; private Integer numTestCases; diff --git a/src/main/java/com/codejoust/main/dto/game/SubmissionRequest.java b/src/main/java/com/codejoust/main/dto/game/SubmissionRequest.java index 535ce8445..2628cbed8 100644 --- a/src/main/java/com/codejoust/main/dto/game/SubmissionRequest.java +++ b/src/main/java/com/codejoust/main/dto/game/SubmissionRequest.java @@ -13,5 +13,5 @@ public class SubmissionRequest { private String code; private String input; private UserDto initiator; - private int problem = 0; + private int problemIndex = 0; } diff --git a/src/main/java/com/codejoust/main/game_object/Submission.java b/src/main/java/com/codejoust/main/game_object/Submission.java index cf28a3f6d..fc7f4d8a9 100644 --- a/src/main/java/com/codejoust/main/game_object/Submission.java +++ b/src/main/java/com/codejoust/main/game_object/Submission.java @@ -13,6 +13,7 @@ public class Submission { private PlayerCode playerCode; + private int problemIndex; private List results; diff --git a/src/main/java/com/codejoust/main/service/GameManagementService.java b/src/main/java/com/codejoust/main/service/GameManagementService.java index d86527187..58f096eb5 100644 --- a/src/main/java/com/codejoust/main/service/GameManagementService.java +++ b/src/main/java/com/codejoust/main/service/GameManagementService.java @@ -185,7 +185,7 @@ public SubmissionDto runCode(String roomId, SubmissionRequest request) { throw new ApiException(GameError.EMPTY_FIELD); } - if (request.getProblem() >= game.getProblems().size() || request.getProblem() < 0) { + if (request.getProblemIndex() >= game.getProblems().size() || request.getProblemIndex() < 0) { throw new ApiException(GameError.BAD_SETTING); } @@ -205,7 +205,7 @@ public SubmissionDto submitSolution(String roomId, SubmissionRequest request) { throw new ApiException(GameError.EMPTY_FIELD); } - if (request.getProblem() >= game.getProblems().size() || request.getProblem() < 0) { + if (request.getProblemIndex() >= game.getProblems().size() || request.getProblemIndex() < 0) { throw new ApiException(GameError.BAD_SETTING); } diff --git a/src/main/java/com/codejoust/main/service/SubmitService.java b/src/main/java/com/codejoust/main/service/SubmitService.java index d9ce2a397..dbd3d79aa 100644 --- a/src/main/java/com/codejoust/main/service/SubmitService.java +++ b/src/main/java/com/codejoust/main/service/SubmitService.java @@ -98,15 +98,13 @@ public SubmissionDto runCode(Game game, SubmissionRequest request) { playerCode.setCode(request.getCode()); playerCode.setLanguage(request.getLanguage()); - player.setPlayerCode(playerCode); - // Make a call to the tester service TesterRequest testerRequest = new TesterRequest(); testerRequest.setCode(request.getCode()); testerRequest.setLanguage(request.getLanguage()); // Set the problem with the single provided test case. - ProblemDto problemDto = getStrippedProblemDto(game.getProblems().get(request.getProblem())); + ProblemDto problemDto = getStrippedProblemDto(game.getProblems().get(request.getProblemIndex())); /** * Provide a temporary output to circumvent output parsing error. @@ -126,6 +124,8 @@ public SubmissionDto runCode(Game game, SubmissionRequest request) { // Return submission, and no further records necessary for running code. Submission submission = getSubmission(testerRequest); + submission.setProblemIndex(request.getProblemIndex()); + player.getSubmissions().add(submission); return GameMapper.submissionToDto(submission); } @@ -138,18 +138,17 @@ public SubmissionDto submitSolution(Game game, SubmissionRequest request) { playerCode.setCode(request.getCode()); playerCode.setLanguage(request.getLanguage()); - player.setPlayerCode(playerCode); - // Make a call to the tester service TesterRequest testerRequest = new TesterRequest(); testerRequest.setCode(request.getCode()); testerRequest.setLanguage(request.getLanguage()); // Invariant: Games have at least one problem (else it will fail to create) - ProblemDto problemDto = getStrippedProblemDto(game.getProblems().get(request.getProblem())); + ProblemDto problemDto = getStrippedProblemDto(game.getProblems().get(request.getProblemIndex())); testerRequest.setProblem(problemDto); Submission submission = getSubmission(testerRequest); + submission.setProblemIndex(request.getProblemIndex()); player.getSubmissions().add(submission); if (submission.getNumCorrect().equals(submission.getNumTestCases())) { diff --git a/src/test/java/com/codejoust/main/api/GameTests.java b/src/test/java/com/codejoust/main/api/GameTests.java index f872c4e43..e718b6767 100644 --- a/src/test/java/com/codejoust/main/api/GameTests.java +++ b/src/test/java/com/codejoust/main/api/GameTests.java @@ -341,7 +341,7 @@ public void runCodeSuccess() throws Exception { // Confirm that running the code does not create a submission. assertEquals(1, gameDto.getPlayers().size()); PlayerDto player = gameDto.getPlayers().get(0); - assertEquals(0, player.getSubmissions().size()); + // assertEquals(0, player.getSubmissions().size()); assertFalse(gameDto.getAllSolved()); } diff --git a/src/test/java/com/codejoust/main/mapper/GameMapperTests.java b/src/test/java/com/codejoust/main/mapper/GameMapperTests.java index 13bcba838..44000a805 100644 --- a/src/test/java/com/codejoust/main/mapper/GameMapperTests.java +++ b/src/test/java/com/codejoust/main/mapper/GameMapperTests.java @@ -123,8 +123,6 @@ public void toDto() { PlayerDto playerDto = gameDto.getPlayers().get(0); assertEquals(UserMapper.toDto(user), playerDto.getUser()); assertEquals(player.getSolved(), playerDto.getSolved()); - assertEquals(playerCode.getCode(), playerDto.getCode()); - assertEquals(playerCode.getLanguage(), playerDto.getLanguage()); assertEquals(1, playerDto.getSubmissions().size()); assertEquals(player.getColor(), playerDto.getColor()); From 2788e35b4fea6c63ad8951ff88602968e7a382e2 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Tue, 11 May 2021 21:00:04 -0700 Subject: [PATCH 20/37] some style changes --- frontend/src/views/Game.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index f557e73bb..1296ef625 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -116,7 +116,7 @@ function GamePage() { const [players, setPlayers] = useState([]); const [gameTimer, setGameTimer] = useState(null); const [problems, setProblems] = useState([]); - const [languageList] = useState([Language.Python]); + const [languageList, setLanguageList] = useState([Language.Python]); const [codeList, setCodeList] = useState(['']); const [currentSubmission, setCurrentSubmission] = useState(null); const [currentProblem, setCurrentProblem] = useState(0); @@ -193,10 +193,12 @@ function GamePage() { // Get the result of promises and set the default code list. Promise.all(promises).then((result) => { const newCodeList = []; + const newLanguageList = []; const codeMap = result; for (let i = 0; i < result.length; i += 1) { newCodeList.push(result[i][Language.Python]); + newLanguageList.push(Language.Python); } // If previous code and language specified, save those as defaults @@ -206,18 +208,19 @@ function GamePage() { if (temp != null) { newCodeList[i] = temp.code; codeMap[i][temp.language as Language] = temp.code; - languageList[i] = temp.language as Language; + newLanguageList[i] = temp.language as Language; setCurrentProblem(i); } } // Set this user's current code setCodeList(newCodeList); + setLanguageList(newLanguageList); setDefaultCodeList(codeMap); }).catch((err) => { setError(err.message); }); - }, [setDefaultCodeList, setCodeList, languageList]); + }, [setDefaultCodeList, setCodeList, setLanguageList]); /** * Display the notification as a callback from the notification From e2927f2e9dae2771e91a4b076ae2788b041b582c Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Tue, 18 May 2021 21:05:06 -0700 Subject: [PATCH 21/37] responding to nits on PR --- frontend/src/views/Game.tsx | 57 ++++++++++--------- .../com/codejoust/main/api/GameTests.java | 1 - 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index 1296ef625..3e0b2156b 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -116,10 +116,10 @@ function GamePage() { const [players, setPlayers] = useState([]); const [gameTimer, setGameTimer] = useState(null); const [problems, setProblems] = useState([]); - const [languageList, setLanguageList] = useState([Language.Python]); + const [languageList, setLanguageList] = useState([Language.Java]); const [codeList, setCodeList] = useState(['']); const [currentSubmission, setCurrentSubmission] = useState(null); - const [currentProblem, setCurrentProblem] = useState(0); + const [currentProblemIndex, setCurrentProblem] = useState(0); const [timeUp, setTimeUp] = useState(false); const [allSolved, setAllSolved] = useState(false); const [defaultCodeList, setDefaultCodeList] = useState([]); @@ -152,7 +152,7 @@ function GamePage() { const createCodeLanguageArray = () => { while (languageList.length < problems.length) { - languageList.push(Language.Python); + languageList.push(Language.Java); } while (codeList.length < problems.length) { @@ -163,13 +163,14 @@ function GamePage() { createCodeLanguageArray(); const setOneCurrentLanguage = (newLanguage: Language) => { - languageList[currentProblem] = newLanguage; + languageList[currentProblemIndex] = newLanguage; }; const setOneCurrentCode = (newCode: string) => { - codeList[currentProblem] = newCode; + codeList[currentProblemIndex] = newCode; }; + // Returns the most recent submission made for problem of index curr. const getSubmission = (curr: number, playerSubmissions: Submission[]) => { for (let i = playerSubmissions.length - 1; i >= 0; i -= 1) { if (playerSubmissions[i].problemIndex === curr) { @@ -197,8 +198,8 @@ function GamePage() { const codeMap = result; for (let i = 0; i < result.length; i += 1) { - newCodeList.push(result[i][Language.Python]); - newLanguageList.push(Language.Python); + newCodeList.push(result[i][Language.Java]); + newLanguageList.push(Language.Java); } // If previous code and language specified, save those as defaults @@ -358,9 +359,9 @@ function GamePage() { const request = { initiator: currentUser!, input, - code: codeList[currentProblem], - language: languageList[currentProblem], - problemIndex: currentProblem, + code: codeList[currentProblemIndex], + language: languageList[currentProblemIndex], + problemIndex: currentProblemIndex, }; runSolution(roomId, request) @@ -370,7 +371,7 @@ function GamePage() { // Set the 'test' submission type to correctly display result. res.submissionType = SubmissionType.Test; submissions.push(res); - setCurrentSubmission(getSubmission(currentProblem, submissions)); + setCurrentSubmission(getSubmission(currentProblemIndex, submissions)); checkSendTestCorrectNotification(res); }) .catch((err) => { @@ -385,9 +386,9 @@ function GamePage() { setError(''); const request = { initiator: currentUser!, - code: codeList[currentProblem], - language: languageList[currentProblem], - problemIndex: currentProblem, + code: codeList[currentProblemIndex], + language: languageList[currentProblemIndex], + problemIndex: currentProblemIndex, }; submitSolution(roomId, request) @@ -397,7 +398,7 @@ function GamePage() { // Set the 'submit' submission type to correctly display result. res.submissionType = SubmissionType.Submit; submissions.push(res); - setCurrentSubmission(getSubmission(currentProblem, submissions)); + setCurrentSubmission(getSubmission(currentProblemIndex, submissions)); checkSendSolutionCorrectNotification(res); }) .catch((err) => { @@ -407,19 +408,19 @@ function GamePage() { }; const nextProblem = () => { - setCurrentProblem((currentProblem + 1) % problems?.length); - setCurrentSubmission(getSubmission(currentProblem, submissions)); + setCurrentProblem((currentProblemIndex + 1) % problems?.length); + setCurrentSubmission(getSubmission(currentProblemIndex, submissions)); }; const previousProblem = () => { - let temp = currentProblem - 1; + let temp = currentProblemIndex - 1; if (temp < 0) { temp += problems?.length; } setCurrentProblem(temp); - setCurrentSubmission(getSubmission(currentProblem, submissions)); + setCurrentSubmission(getSubmission(currentProblemIndex, submissions)); }; const displayPlayerLeaderboard = useCallback(() => players.map((player, index) => ( @@ -482,19 +483,19 @@ function GamePage() { > {/* Problem title/description panel */} - {problems[currentProblem]?.name} - {problems[currentProblem] ? ( + {problems[currentProblemIndex]?.name} + {problems[currentProblemIndex] ? ( - {displayNameFromDifficulty(problems[currentProblem].difficulty!)} + {displayNameFromDifficulty(problems[currentProblemIndex].difficulty!)} ) : null} ''} readOnly /> @@ -523,17 +524,17 @@ function GamePage() { languageList[currentProblem]} + getCurrentLanguage={() => languageList[currentProblemIndex]} defaultCodeMap={defaultCodeList} - currentProblem={currentProblem} - defaultLanguage={Language.Python} + currentProblem={currentProblemIndex} + defaultLanguage={Language.Java} defaultCode={null} /> Date: Wed, 26 May 2021 21:50:32 -0700 Subject: [PATCH 22/37] committing and pushing to create a branch --- .../src/components/card/LeaderboardCard.tsx | 22 ++++---- .../components/results/PlayerResultsItem.tsx | 11 ++-- frontend/src/components/results/Podium.tsx | 4 +- frontend/src/util/Hook.tsx | 53 +++++++++++++++++-- frontend/src/views/Game.tsx | 15 ++++-- .../codejoust/main/dto/game/GameMapper.java | 1 + .../codejoust/main/game_object/Player.java | 3 +- .../codejoust/main/service/SubmitService.java | 10 ++-- 8 files changed, 89 insertions(+), 30 deletions(-) diff --git a/frontend/src/components/card/LeaderboardCard.tsx b/frontend/src/components/card/LeaderboardCard.tsx index aaceba3a6..cb0c0f7ed 100644 --- a/frontend/src/components/card/LeaderboardCard.tsx +++ b/frontend/src/components/card/LeaderboardCard.tsx @@ -4,7 +4,7 @@ import { Player } from '../../api/Game'; import { LowMarginText, SmallText } from '../core/Text'; import PlayerIcon from './PlayerIcon'; import { Color } from '../../api/Color'; -import useBestSubmission from '../../util/Hook'; +import useGetScore, { useGetSubmissionTime } from '../../util/Hook'; type ContentStyleType = { isCurrentPlayer: boolean, @@ -67,37 +67,39 @@ type LeaderboardCardProps = { isCurrentPlayer: boolean, place: number, color: Color, + numProblems: number, }; function LeaderboardCard(props: LeaderboardCardProps) { const { - place, player, isCurrentPlayer, color, + place, player, isCurrentPlayer, color, numProblems, } = props; const [showHover, setShowHover] = useState(false); - const bestSubmission = useBestSubmission(player); + const score = useGetScore(player); + const time = useGetSubmissionTime(player); const getScoreDisplay = () => { - if (!bestSubmission) { - return '0'; + if (!score) { + return 0; } - return `${bestSubmission.numCorrect}/${bestSubmission.numTestCases}`; + return score; }; const getScorePercentage = () => { - if (!bestSubmission) { + if (!score) { return ''; } - return ` ${Math.round((bestSubmission.numCorrect / bestSubmission.numTestCases) * 100)}%`; + return ` ${Math.round((score / numProblems) * 100)}%`; }; const getSubmissionTime = () => { - if (!bestSubmission) { + if (!time) { return 'Never'; } const currentTime = new Date().getTime(); - const diffMilliseconds = currentTime - new Date(bestSubmission.startTime).getTime(); + const diffMilliseconds = currentTime - new Date(time).getTime(); const diffMinutes = Math.floor(diffMilliseconds / (60 * 1000)); return `${diffMinutes}m ago`; }; diff --git a/frontend/src/components/results/PlayerResultsItem.tsx b/frontend/src/components/results/PlayerResultsItem.tsx index 65de9ec82..26ac76598 100644 --- a/frontend/src/components/results/PlayerResultsItem.tsx +++ b/frontend/src/components/results/PlayerResultsItem.tsx @@ -1,9 +1,9 @@ import React from 'react'; import styled from 'styled-components'; -import { Player } from '../../api/Game'; +import { Player, Submission } from '../../api/Game'; import { LowMarginText, Text } from '../core/Text'; import { Color } from '../../api/Color'; -import useBestSubmission from '../../util/Hook'; +import useGetScore, { useBestSubmission } from '../../util/Hook'; import Language, { displayNameFromLanguage } from '../../api/Language'; import { TextButton } from '../core/Button'; @@ -90,7 +90,8 @@ function PlayerResultsItem(props: PlayerResultsCardProps) { player, place, isCurrentPlayer, color, gameStartTime, onViewCode, } = props; - const bestSubmission = useBestSubmission(player); + const score = useGetScore(player); + const bestSubmission : Submission | null = useBestSubmission(player); const getDisplayNickname = () => { const { nickname } = player.user; @@ -98,11 +99,11 @@ function PlayerResultsItem(props: PlayerResultsCardProps) { }; const getScore = () => { - if (!bestSubmission) { + if (!score) { return '0'; } - const percent = Math.round((bestSubmission.numCorrect / bestSubmission.numTestCases) * 100); + const percent = Math.round((score / 12321) * 100); return `${percent}%`; }; diff --git a/frontend/src/components/results/Podium.tsx b/frontend/src/components/results/Podium.tsx index 02cb1ef64..edbf52aaa 100644 --- a/frontend/src/components/results/Podium.tsx +++ b/frontend/src/components/results/Podium.tsx @@ -1,9 +1,9 @@ import React from 'react'; import styled from 'styled-components'; -import { Player } from '../../api/Game'; +import { Player, Submission } from '../../api/Game'; import { Text, MediumText } from '../core/Text'; import Language, { displayNameFromLanguage } from '../../api/Language'; -import useBestSubmission from '../../util/Hook'; +import { useBestSubmission } from '../../util/Hook'; type PodiumProps = { place: number, diff --git a/frontend/src/util/Hook.tsx b/frontend/src/util/Hook.tsx index 3346f0f17..21c5cfb11 100644 --- a/frontend/src/util/Hook.tsx +++ b/frontend/src/util/Hook.tsx @@ -1,7 +1,7 @@ import { useState, useEffect } from 'react'; -import { Player, Submission } from '../api/Game'; +import { Player, Submission, SubmissionType } from '../api/Game'; -const useBestSubmission = (player?: Player) => { +const useBestSubmission = (player?: Player) : Submission | null => { const [bestSubmission, setBestSubmission] = useState(null); useEffect(() => { @@ -22,4 +22,51 @@ const useBestSubmission = (player?: Player) => { return bestSubmission; }; -export default useBestSubmission; +const useGetScore = (player?: Player) => { + const counted = new Set(); + + useEffect(() => { + if (player) { + for (let i = 0; i < player.submissions.length; i += 1) { + if (player.submissions[i].submissionType === SubmissionType.Submit && + player.submissions[i].numCorrect === player.submissions[i].numTestCases && + !counted.has(player.submissions[i].problemIndex)) { + counted.add(player.submissions[i].problemIndex); + } + } + } + }, [player]); + + if (player == null || player.submissions.length === 0) { + return null; + } + + return counted.size; +}; + +export const useGetSubmissionTime = (player?: Player) => { + const counted = new Set(); + let time; + + useEffect(() => { + if (player) { + for (let i = 0; i < player.submissions.length; i += 1) { + if (player.submissions[i].submissionType === SubmissionType.Submit && + player.submissions[i].numCorrect === player.submissions[i].numTestCases && + !counted.has(player.submissions[i].problemIndex)) { + counted.add(player.submissions[i].problemIndex); + time = player.submissions[i].startTime; + } + } + } + }, [player]); + + if (!time && player) { + time = player.submissions[player.submissions.length - 1].startTime; + } + + return time; +}; + +export default useGetScore; +export { useBestSubmission }; diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index 3e0b2156b..b8c88e901 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -119,7 +119,7 @@ function GamePage() { const [languageList, setLanguageList] = useState([Language.Java]); const [codeList, setCodeList] = useState(['']); const [currentSubmission, setCurrentSubmission] = useState(null); - const [currentProblemIndex, setCurrentProblem] = useState(0); + const [currentProblemIndex, setCurrentProblemIndex] = useState(0); const [timeUp, setTimeUp] = useState(false); const [allSolved, setAllSolved] = useState(false); const [defaultCodeList, setDefaultCodeList] = useState([]); @@ -151,6 +151,12 @@ function GamePage() { }; const createCodeLanguageArray = () => { + /* + Here is the console log statement I added that was being executed several times. + The languageList.length kept throwing a null pointer exception, despite being defined above. + */ + console.log(languageList); + while (languageList.length < problems.length) { languageList.push(Language.Java); } @@ -210,7 +216,7 @@ function GamePage() { newCodeList[i] = temp.code; codeMap[i][temp.language as Language] = temp.code; newLanguageList[i] = temp.language as Language; - setCurrentProblem(i); + setCurrentProblemIndex(i); } } @@ -408,7 +414,7 @@ function GamePage() { }; const nextProblem = () => { - setCurrentProblem((currentProblemIndex + 1) % problems?.length); + setCurrentProblemIndex((currentProblemIndex + 1) % problems?.length); setCurrentSubmission(getSubmission(currentProblemIndex, submissions)); }; @@ -419,7 +425,7 @@ function GamePage() { temp += problems?.length; } - setCurrentProblem(temp); + setCurrentProblemIndex(temp); setCurrentSubmission(getSubmission(currentProblemIndex, submissions)); }; @@ -429,6 +435,7 @@ function GamePage() { isCurrentPlayer={player.user.userId === currentUser?.userId} place={index + 1} color={player.color} + numProblems={problems.length} /> )), [players, currentUser]); diff --git a/src/main/java/com/codejoust/main/dto/game/GameMapper.java b/src/main/java/com/codejoust/main/dto/game/GameMapper.java index eeb710235..630bbad55 100644 --- a/src/main/java/com/codejoust/main/dto/game/GameMapper.java +++ b/src/main/java/com/codejoust/main/dto/game/GameMapper.java @@ -68,6 +68,7 @@ public static Game fromRoom(Room room) { for (User user : room.getUsers()) { Player player = PlayerMapper.playerFromUser(user); player.setColor(colorList.get(index)); + player.setSolved(new Boolean[room.getNumProblems()]); players.put(user.getUserId(), player); index = (index + 1) % colorList.size(); } diff --git a/src/main/java/com/codejoust/main/game_object/Player.java b/src/main/java/com/codejoust/main/game_object/Player.java index c5db6761c..a86534caa 100644 --- a/src/main/java/com/codejoust/main/game_object/Player.java +++ b/src/main/java/com/codejoust/main/game_object/Player.java @@ -28,9 +28,8 @@ public class Player { * Solved variable if the user has successfully solved the problem, * or is still competing. */ - private Boolean solved = false; + private Boolean[] solved; // Color associated with this player, generated on backend in game start. private Color color; - } diff --git a/src/main/java/com/codejoust/main/service/SubmitService.java b/src/main/java/com/codejoust/main/service/SubmitService.java index dbd3d79aa..29b9ce1a3 100644 --- a/src/main/java/com/codejoust/main/service/SubmitService.java +++ b/src/main/java/com/codejoust/main/service/SubmitService.java @@ -152,15 +152,17 @@ public SubmissionDto submitSolution(Game game, SubmissionRequest request) { player.getSubmissions().add(submission); if (submission.getNumCorrect().equals(submission.getNumTestCases())) { - player.setSolved(true); + player.getSolved()[request.getProblemIndex()] = true; } // Variable to indicate whether all players have solved the problem. boolean allSolved = true; for (Player p : game.getPlayers().values()) { - if (p.getSolved() == null || !p.getSolved()) { - allSolved = false; - break; + for (Boolean b : p.getSolved()) { + if (b == null || !b) { + allSolved = false; + break; + } } } From 2341428803758269f5b257cf8a2f7f9ff55be971 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Thu, 27 May 2021 17:33:51 -0700 Subject: [PATCH 23/37] Fixing backend compilation --- frontend/src/util/Hook.tsx | 2 +- src/main/java/com/codejoust/main/dto/game/PlayerDto.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/util/Hook.tsx b/frontend/src/util/Hook.tsx index 21c5cfb11..beafd28a1 100644 --- a/frontend/src/util/Hook.tsx +++ b/frontend/src/util/Hook.tsx @@ -61,7 +61,7 @@ export const useGetSubmissionTime = (player?: Player) => { } }, [player]); - if (!time && player) { + if (!time && player && player.submissions.length > 0) { time = player.submissions[player.submissions.length - 1].startTime; } diff --git a/src/main/java/com/codejoust/main/dto/game/PlayerDto.java b/src/main/java/com/codejoust/main/dto/game/PlayerDto.java index 163097c4c..0d1cad624 100644 --- a/src/main/java/com/codejoust/main/dto/game/PlayerDto.java +++ b/src/main/java/com/codejoust/main/dto/game/PlayerDto.java @@ -19,6 +19,6 @@ public class PlayerDto { private String code; private CodeLanguage language; private List submissions = new ArrayList<>(); - private Boolean solved; + private Boolean[] solved; private Color color; } From 4533ef30777b58bca18e6cdc5f0f3f64bdca611d Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Sun, 6 Jun 2021 18:41:31 -0700 Subject: [PATCH 24/37] works finally, fixed some, but not all bugs --- .../components/results/PlayerResultsItem.tsx | 20 ++++++++-------- frontend/src/components/results/Podium.tsx | 17 ++++++++------ .../src/components/results/ResultsTable.tsx | 4 +++- frontend/src/util/Hook.tsx | 23 +++++++++++-------- frontend/src/views/Game.tsx | 15 ++++-------- frontend/src/views/Results.tsx | 4 ++++ .../codejoust/main/dto/game/GameMapper.java | 2 +- .../codejoust/main/dto/game/PlayerDto.java | 2 +- .../codejoust/main/game_object/Player.java | 2 +- .../main/mapper/GameMapperTests.java | 2 +- .../main/mapper/PlayerMapperTests.java | 3 +-- .../service/GameManagementServiceTests.java | 2 +- .../main/service/RoomServiceTests.java | 10 ++++---- 13 files changed, 56 insertions(+), 50 deletions(-) diff --git a/frontend/src/components/results/PlayerResultsItem.tsx b/frontend/src/components/results/PlayerResultsItem.tsx index 26ac76598..93bca2c38 100644 --- a/frontend/src/components/results/PlayerResultsItem.tsx +++ b/frontend/src/components/results/PlayerResultsItem.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components'; import { Player, Submission } from '../../api/Game'; import { LowMarginText, Text } from '../core/Text'; import { Color } from '../../api/Color'; -import useGetScore, { useBestSubmission } from '../../util/Hook'; +import useGetScore, { useBestSubmission, useGetSubmissionTime } from '../../util/Hook'; import Language, { displayNameFromLanguage } from '../../api/Language'; import { TextButton } from '../core/Button'; @@ -82,15 +82,17 @@ type PlayerResultsCardProps = { isCurrentPlayer: boolean, gameStartTime: string, color: Color, + numProblems: number, onViewCode: () => void, }; function PlayerResultsItem(props: PlayerResultsCardProps) { const { - player, place, isCurrentPlayer, color, gameStartTime, onViewCode, + player, place, isCurrentPlayer, color, gameStartTime, numProblems, onViewCode, } = props; const score = useGetScore(player); + const time = useGetSubmissionTime(player); const bestSubmission : Submission | null = useBestSubmission(player); const getDisplayNickname = () => { @@ -103,21 +105,19 @@ function PlayerResultsItem(props: PlayerResultsCardProps) { return '0'; } - const percent = Math.round((score / 12321) * 100); + const percent = Math.round((score / numProblems) * 100); return `${percent}%`; }; const getSubmissionTime = () => { - if (!bestSubmission) { - return 'N/A'; + if (!time) { + return 'Never'; } - // Calculate time from start of game till best submission - const startTime = new Date(gameStartTime).getTime(); - const diffMilliseconds = new Date(bestSubmission.startTime).getTime() - startTime; + const currentTime = new Date().getTime(); + const diffMilliseconds = currentTime - new Date(time).getTime(); const diffMinutes = Math.floor(diffMilliseconds / (60 * 1000)); - - return ` ${diffMinutes} min`; + return `${diffMinutes}m ago`; }; const getSubmissionCount = () => player.submissions.length || '0'; diff --git a/frontend/src/components/results/Podium.tsx b/frontend/src/components/results/Podium.tsx index edbf52aaa..020b9b3db 100644 --- a/frontend/src/components/results/Podium.tsx +++ b/frontend/src/components/results/Podium.tsx @@ -1,9 +1,9 @@ import React from 'react'; import styled from 'styled-components'; -import { Player, Submission } from '../../api/Game'; +import { Player } from '../../api/Game'; import { Text, MediumText } from '../core/Text'; import Language, { displayNameFromLanguage } from '../../api/Language'; -import { useBestSubmission } from '../../util/Hook'; +import useGetScore, { useBestSubmission, useGetSubmissionTime } from '../../util/Hook'; type PodiumProps = { place: number, @@ -12,6 +12,7 @@ type PodiumProps = { inviteContent: React.ReactNode, loading: boolean, isCurrentPlayer: boolean, + numProblems: number, }; type MedalProps = { @@ -85,10 +86,12 @@ const Medal = styled.div` function Podium(props: PodiumProps) { const { - place, player, gameStartTime, loading, inviteContent, isCurrentPlayer, + place, player, gameStartTime, loading, inviteContent, isCurrentPlayer, numProblems, } = props; + const score = useGetScore(player); const bestSubmission = useBestSubmission(player); + const time = useGetSubmissionTime(player); const getMedalColor = () => { switch (place) { @@ -120,7 +123,7 @@ function Podium(props: PodiumProps) { return inviteContent; } - if (!bestSubmission) { + if (!score) { return ( Scored @@ -130,7 +133,7 @@ function Podium(props: PodiumProps) { ); } - const percent = Math.round((bestSubmission.numCorrect / bestSubmission.numTestCases) * 100); + const percent = Math.round((score / numProblems) * 100); return ( Scored @@ -140,13 +143,13 @@ function Podium(props: PodiumProps) { }; const getTimeText = () => { - if (!bestSubmission) { + if (!time) { return ; } // Calculate time from start of game till best submission const startTime = new Date(gameStartTime).getTime(); - const diffMilliseconds = new Date(bestSubmission.startTime).getTime() - startTime; + const diffMilliseconds = new Date(time).getTime() - startTime; const diffMinutes = Math.floor(diffMilliseconds / (60 * 1000)); return ( diff --git a/frontend/src/components/results/ResultsTable.tsx b/frontend/src/components/results/ResultsTable.tsx index c88a1c2fb..289791133 100644 --- a/frontend/src/components/results/ResultsTable.tsx +++ b/frontend/src/components/results/ResultsTable.tsx @@ -35,12 +35,13 @@ type ResultsTableProps = { players: Player[], currentUser: User | null, gameStartTime: string, + numProblems: number, viewPlayerCode: (index: number) => void, }; function ResultsTable(props: ResultsTableProps) { const { - players, currentUser, gameStartTime, viewPlayerCode, + players, currentUser, gameStartTime, numProblems, viewPlayerCode, } = props; return ( @@ -60,6 +61,7 @@ function ResultsTable(props: ResultsTableProps) { isCurrentPlayer={currentUser?.userId === player.user.userId} gameStartTime={gameStartTime} color={player.color} + numProblems={numProblems} onViewCode={() => viewPlayerCode(index)} /> ))} diff --git a/frontend/src/util/Hook.tsx b/frontend/src/util/Hook.tsx index 90ae92c3a..5cf8c0e5c 100644 --- a/frontend/src/util/Hook.tsx +++ b/frontend/src/util/Hook.tsx @@ -27,7 +27,8 @@ export const useBestSubmission = (player?: Player) => { }; const useGetScore = (player?: Player) => { - const counted = new Set(); + const counted = new Set(); + const [score, setScore] = useState(0); useEffect(() => { if (player) { @@ -37,37 +38,39 @@ const useGetScore = (player?: Player) => { counted.add(player.submissions[i].problemIndex); } } + + setScore(counted.size); } - }, [player]); + + }, [player, setScore]); if (player == null || player.submissions.length === 0) { return null; } - - return counted.size; + + return score; }; export default useGetScore; export const useGetSubmissionTime = (player?: Player) => { - const counted = new Set(); - let time; + const counted = new Set(); + const [time, setTime] = useState(); useEffect(() => { if (player) { for (let i = 0; i < player.submissions.length; i += 1) { - if (player.submissions[i].submissionType === SubmissionType.Submit && - player.submissions[i].numCorrect === player.submissions[i].numTestCases && + if (player.submissions[i].numCorrect === player.submissions[i].numTestCases && !counted.has(player.submissions[i].problemIndex)) { counted.add(player.submissions[i].problemIndex); - time = player.submissions[i].startTime; + setTime(player.submissions[i].startTime); } } } }, [player]); if (!time && player && player.submissions.length > 0) { - time = player.submissions[player.submissions.length - 1].startTime; + setTime(player.submissions[player.submissions.length - 1].startTime); } return time; diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index 5b8df8ba0..0aa853320 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -159,12 +159,6 @@ function GamePage() { }; const createCodeLanguageArray = () => { - /* - Here is the console log statement I added that was being executed several times. - The languageList.length kept throwing a null pointer exception, despite being defined above. - */ - console.log(languageList); - while (languageList.length < problems.length) { languageList.push(Language.Java); } @@ -396,8 +390,8 @@ function GamePage() { setLoading(false); // Set the 'test' submission type to correctly display result. - submissions[submissions.length - 1].submissionType = SubmissionType.Test; - setCurrentSubmission(getSubmission(currentProblemIndex, submissions)); + res.submissionType = SubmissionType.Test; + setCurrentSubmission(res); checkSendTestCorrectNotification(res); }) .catch((err) => { @@ -422,7 +416,8 @@ function GamePage() { setLoading(false); // Set the 'submit' submission type to correctly display result. - submissions[submissions.length - 1].submissionType = SubmissionType.Submit; + res.submissionType = SubmissionType.Submit; + setSubmissions(submissions.concat([res])); setCurrentSubmission(getSubmission(currentProblemIndex, submissions)); checkSendSolutionCorrectNotification(res); }) @@ -466,7 +461,7 @@ function GamePage() { color={player.color} numProblems={problems.length} /> - )), [players, currentUser]); + )), [players, currentUser, problems.length]); // Subscribe user to primary socket and to notifications. useEffect(() => { diff --git a/frontend/src/views/Results.tsx b/frontend/src/views/Results.tsx index 6eb966895..787783d31 100644 --- a/frontend/src/views/Results.tsx +++ b/frontend/src/views/Results.tsx @@ -356,6 +356,7 @@ function GameResultsPage() { inviteContent={inviteContent()} loading={loading} isCurrentPlayer={players[1]?.user.userId === currentUser?.userId} + numProblems={game?.problems.length || 1} /> @@ -414,6 +417,7 @@ function GameResultsPage() { players={players} currentUser={currentUser} gameStartTime={startTime} + numProblems={game?.problems.length || 1} viewPlayerCode={(index: number) => setCodeModal(index)} /> ) : null} diff --git a/src/main/java/com/codejoust/main/dto/game/GameMapper.java b/src/main/java/com/codejoust/main/dto/game/GameMapper.java index 438d708a8..a7afe9f2c 100644 --- a/src/main/java/com/codejoust/main/dto/game/GameMapper.java +++ b/src/main/java/com/codejoust/main/dto/game/GameMapper.java @@ -69,7 +69,7 @@ public static Game fromRoom(Room room) { for (User user : room.getUsers()) { Player player = PlayerMapper.playerFromUser(user); player.setColor(colorList.get(index)); - player.setSolved(new Boolean[room.getNumProblems()]); + player.setSolved(new boolean[room.getNumProblems()]); players.put(user.getUserId(), player); index = (index + 1) % colorList.size(); } diff --git a/src/main/java/com/codejoust/main/dto/game/PlayerDto.java b/src/main/java/com/codejoust/main/dto/game/PlayerDto.java index 0d1cad624..474d2acb9 100644 --- a/src/main/java/com/codejoust/main/dto/game/PlayerDto.java +++ b/src/main/java/com/codejoust/main/dto/game/PlayerDto.java @@ -19,6 +19,6 @@ public class PlayerDto { private String code; private CodeLanguage language; private List submissions = new ArrayList<>(); - private Boolean[] solved; + private boolean[] solved; private Color color; } diff --git a/src/main/java/com/codejoust/main/game_object/Player.java b/src/main/java/com/codejoust/main/game_object/Player.java index a86534caa..bb9b9f18e 100644 --- a/src/main/java/com/codejoust/main/game_object/Player.java +++ b/src/main/java/com/codejoust/main/game_object/Player.java @@ -28,7 +28,7 @@ public class Player { * Solved variable if the user has successfully solved the problem, * or is still competing. */ - private Boolean[] solved; + private boolean[] solved; // Color associated with this player, generated on backend in game start. private Color color; diff --git a/src/test/java/com/codejoust/main/mapper/GameMapperTests.java b/src/test/java/com/codejoust/main/mapper/GameMapperTests.java index 1d5d0b79e..2d7d008ba 100644 --- a/src/test/java/com/codejoust/main/mapper/GameMapperTests.java +++ b/src/test/java/com/codejoust/main/mapper/GameMapperTests.java @@ -98,7 +98,7 @@ public void toDto() { submission.setNumCorrect(TEST_CASES); Player player = game.getPlayers().get(TestFields.USER_ID); - player.setSolved(true); + player.setSolved(new boolean[]{true}); player.setPlayerCode(playerCode); player.getSubmissions().add(submission); diff --git a/src/test/java/com/codejoust/main/mapper/PlayerMapperTests.java b/src/test/java/com/codejoust/main/mapper/PlayerMapperTests.java index 62f5161a3..8a8343520 100644 --- a/src/test/java/com/codejoust/main/mapper/PlayerMapperTests.java +++ b/src/test/java/com/codejoust/main/mapper/PlayerMapperTests.java @@ -5,7 +5,6 @@ import org.springframework.boot.test.context.SpringBootTest; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; import com.codejoust.main.dto.game.PlayerMapper; @@ -25,7 +24,7 @@ public void playerFromUser() { assertEquals(user, player.getUser()); assertNull(player.getPlayerCode()); - assertFalse(player.getSolved()); + assertEquals(new boolean[]{false}, player.getSolved()); assertEquals(0, player.getSubmissions().size()); } } diff --git a/src/test/java/com/codejoust/main/service/GameManagementServiceTests.java b/src/test/java/com/codejoust/main/service/GameManagementServiceTests.java index fcd8f08c5..6c6e57051 100644 --- a/src/test/java/com/codejoust/main/service/GameManagementServiceTests.java +++ b/src/test/java/com/codejoust/main/service/GameManagementServiceTests.java @@ -87,7 +87,7 @@ private void addSubmissionHelper(Player player, int numCorrect) { player.getSubmissions().add(submission); if (numCorrect == TestFields.NUM_PROBLEMS) { - player.setSolved(true); + player.setSolved(new boolean[]{true}); } } diff --git a/src/test/java/com/codejoust/main/service/RoomServiceTests.java b/src/test/java/com/codejoust/main/service/RoomServiceTests.java index 83caf0c1f..f114df00d 100644 --- a/src/test/java/com/codejoust/main/service/RoomServiceTests.java +++ b/src/test/java/com/codejoust/main/service/RoomServiceTests.java @@ -284,10 +284,10 @@ public void setInvalidNumProblemsFailure() { * set to outside of the allowable range */ User host = new User(); - host.setNickname(NICKNAME); + host.setNickname(TestFields.NICKNAME); Room room = new Room(); - room.setRoomId(ROOM_ID); + room.setRoomId(TestFields.ROOM_ID); room.setHost(host); room.addUser(host); @@ -296,10 +296,10 @@ public void setInvalidNumProblemsFailure() { request.setNumProblems(15); // Mock repository to return room when called - Mockito.doReturn(room).when(repository).findRoomByRoomId(eq(ROOM_ID)); - ApiException exception = assertThrows(ApiException.class, () -> roomService.updateRoomSettings(ROOM_ID, request)); + Mockito.doReturn(room).when(repository).findRoomByRoomId(eq(TestFields.ROOM_ID)); + ApiException exception = assertThrows(ApiException.class, () -> roomService.updateRoomSettings(TestFields.ROOM_ID, request)); - verify(repository).findRoomByRoomId(ROOM_ID); + verify(repository).findRoomByRoomId(TestFields.ROOM_ID); assertEquals(ProblemError.INVALID_NUMBER_REQUEST, exception.getError()); } From 1bf16132d0abe9c4f3d1d38016b24c5318c2868e Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Mon, 7 Jun 2021 19:00:33 -0700 Subject: [PATCH 25/37] more final tweaks --- frontend/src/views/Game.tsx | 6 +++--- src/test/java/com/codejoust/main/api/GameTests.java | 1 + .../java/com/codejoust/main/mapper/GameMapperTests.java | 3 ++- .../java/com/codejoust/main/mapper/PlayerMapperTests.java | 1 - 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index 0aa853320..5e3bfbed9 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -418,7 +418,7 @@ function GamePage() { // Set the 'submit' submission type to correctly display result. res.submissionType = SubmissionType.Submit; setSubmissions(submissions.concat([res])); - setCurrentSubmission(getSubmission(currentProblemIndex, submissions)); + setCurrentSubmission(res); checkSendSolutionCorrectNotification(res); }) .catch((err) => { @@ -429,7 +429,7 @@ function GamePage() { const nextProblem = () => { setCurrentProblemIndex((currentProblemIndex + 1) % problems?.length); - setCurrentSubmission(getSubmission(currentProblemIndex, submissions)); + setCurrentSubmission(getSubmission((currentProblemIndex + 1) % problems?.length, submissions)); }; const previousProblem = () => { @@ -440,7 +440,7 @@ function GamePage() { } setCurrentProblemIndex(temp); - setCurrentSubmission(getSubmission(currentProblemIndex, submissions)); + setCurrentSubmission(getSubmission(temp, submissions)); }; const endGameAction = () => { diff --git a/src/test/java/com/codejoust/main/api/GameTests.java b/src/test/java/com/codejoust/main/api/GameTests.java index 2020f2283..5adaae04f 100644 --- a/src/test/java/com/codejoust/main/api/GameTests.java +++ b/src/test/java/com/codejoust/main/api/GameTests.java @@ -218,6 +218,7 @@ public void runCodeSuccess() throws Exception { // Confirm that running the code does not create a submission. assertEquals(1, gameDto.getPlayers().size()); PlayerDto player = gameDto.getPlayers().get(0); + assertEquals(0, player.getSubmissions().size()); assertFalse(gameDto.getAllSolved()); } diff --git a/src/test/java/com/codejoust/main/mapper/GameMapperTests.java b/src/test/java/com/codejoust/main/mapper/GameMapperTests.java index 2d7d008ba..408db99c2 100644 --- a/src/test/java/com/codejoust/main/mapper/GameMapperTests.java +++ b/src/test/java/com/codejoust/main/mapper/GameMapperTests.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -113,7 +114,7 @@ public void toDto() { PlayerDto playerDto = gameDto.getPlayers().get(0); assertEquals(UserMapper.toDto(user), playerDto.getUser()); - assertEquals(player.getSolved(), playerDto.getSolved()); + assertArrayEquals(player.getSolved(), playerDto.getSolved()); assertEquals(1, playerDto.getSubmissions().size()); assertEquals(player.getColor(), playerDto.getColor()); diff --git a/src/test/java/com/codejoust/main/mapper/PlayerMapperTests.java b/src/test/java/com/codejoust/main/mapper/PlayerMapperTests.java index 8a8343520..2c6e3063c 100644 --- a/src/test/java/com/codejoust/main/mapper/PlayerMapperTests.java +++ b/src/test/java/com/codejoust/main/mapper/PlayerMapperTests.java @@ -24,7 +24,6 @@ public void playerFromUser() { assertEquals(user, player.getUser()); assertNull(player.getPlayerCode()); - assertEquals(new boolean[]{false}, player.getSolved()); assertEquals(0, player.getSubmissions().size()); } } From 8860fdb9508430c76d1278ce66674b5c871b8053 Mon Sep 17 00:00:00 2001 From: Alan Bi Date: Mon, 14 Jun 2021 20:35:38 -0700 Subject: [PATCH 26/37] Address some comments --- frontend/src/components/card/LeaderboardCard.tsx | 2 +- .../src/components/results/PlayerResultsItem.tsx | 8 ++++---- frontend/src/components/results/Podium.tsx | 2 +- frontend/src/util/Hook.tsx | 14 +++++--------- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/frontend/src/components/card/LeaderboardCard.tsx b/frontend/src/components/card/LeaderboardCard.tsx index cb0c0f7ed..b045486a1 100644 --- a/frontend/src/components/card/LeaderboardCard.tsx +++ b/frontend/src/components/card/LeaderboardCard.tsx @@ -4,7 +4,7 @@ import { Player } from '../../api/Game'; import { LowMarginText, SmallText } from '../core/Text'; import PlayerIcon from './PlayerIcon'; import { Color } from '../../api/Color'; -import useGetScore, { useGetSubmissionTime } from '../../util/Hook'; +import { useGetScore, useGetSubmissionTime } from '../../util/Hook'; type ContentStyleType = { isCurrentPlayer: boolean, diff --git a/frontend/src/components/results/PlayerResultsItem.tsx b/frontend/src/components/results/PlayerResultsItem.tsx index 93bca2c38..e493ab251 100644 --- a/frontend/src/components/results/PlayerResultsItem.tsx +++ b/frontend/src/components/results/PlayerResultsItem.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components'; import { Player, Submission } from '../../api/Game'; import { LowMarginText, Text } from '../core/Text'; import { Color } from '../../api/Color'; -import useGetScore, { useBestSubmission, useGetSubmissionTime } from '../../util/Hook'; +import { useBestSubmission, useGetScore, useGetSubmissionTime } from '../../util/Hook'; import Language, { displayNameFromLanguage } from '../../api/Language'; import { TextButton } from '../core/Button'; @@ -111,11 +111,11 @@ function PlayerResultsItem(props: PlayerResultsCardProps) { const getSubmissionTime = () => { if (!time) { - return 'Never'; + return 'N/A'; } - const currentTime = new Date().getTime(); - const diffMilliseconds = currentTime - new Date(time).getTime(); + const startTime = new Date(gameStartTime).getTime(); + const diffMilliseconds = new Date(time).getTime() - startTime; const diffMinutes = Math.floor(diffMilliseconds / (60 * 1000)); return `${diffMinutes}m ago`; }; diff --git a/frontend/src/components/results/Podium.tsx b/frontend/src/components/results/Podium.tsx index 020b9b3db..3aab8cdc9 100644 --- a/frontend/src/components/results/Podium.tsx +++ b/frontend/src/components/results/Podium.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components'; import { Player } from '../../api/Game'; import { Text, MediumText } from '../core/Text'; import Language, { displayNameFromLanguage } from '../../api/Language'; -import useGetScore, { useBestSubmission, useGetSubmissionTime } from '../../util/Hook'; +import { useBestSubmission, useGetScore, useGetSubmissionTime } from '../../util/Hook'; type PodiumProps = { place: number, diff --git a/frontend/src/util/Hook.tsx b/frontend/src/util/Hook.tsx index 5cf8c0e5c..850bb4861 100644 --- a/frontend/src/util/Hook.tsx +++ b/frontend/src/util/Hook.tsx @@ -26,33 +26,29 @@ export const useBestSubmission = (player?: Player) => { return bestSubmission; }; -const useGetScore = (player?: Player) => { +export const useGetScore = (player?: Player) => { const counted = new Set(); const [score, setScore] = useState(0); useEffect(() => { if (player) { for (let i = 0; i < player.submissions.length; i += 1) { - if (player.submissions[i].numCorrect === player.submissions[i].numTestCases && - !counted.has(player.submissions[i].problemIndex)) { + if (player.submissions[i].numCorrect === player.submissions[i].numTestCases + && !counted.has(player.submissions[i].problemIndex)) { counted.add(player.submissions[i].problemIndex); } } setScore(counted.size); } - }, [player, setScore]); if (player == null || player.submissions.length === 0) { return null; } - return score; }; -export default useGetScore; - export const useGetSubmissionTime = (player?: Player) => { const counted = new Set(); const [time, setTime] = useState(); @@ -60,8 +56,8 @@ export const useGetSubmissionTime = (player?: Player) => { useEffect(() => { if (player) { for (let i = 0; i < player.submissions.length; i += 1) { - if (player.submissions[i].numCorrect === player.submissions[i].numTestCases && - !counted.has(player.submissions[i].problemIndex)) { + if (player.submissions[i].numCorrect === player.submissions[i].numTestCases + && !counted.has(player.submissions[i].problemIndex)) { counted.add(player.submissions[i].problemIndex); setTime(player.submissions[i].startTime); } From ed4e3eb4bc5141bc097831b702bb8f49915af843 Mon Sep 17 00:00:00 2001 From: Alan Bi Date: Tue, 15 Jun 2021 21:41:09 -0700 Subject: [PATCH 27/37] Copy over changes to PlayerGameView.tsx file --- .../src/components/game/PlayerGameView.tsx | 151 +++++++++++++----- frontend/src/views/Game.tsx | 141 +--------------- 2 files changed, 117 insertions(+), 175 deletions(-) diff --git a/frontend/src/components/game/PlayerGameView.tsx b/frontend/src/components/game/PlayerGameView.tsx index 46251a721..2585bde3d 100644 --- a/frontend/src/components/game/PlayerGameView.tsx +++ b/frontend/src/components/game/PlayerGameView.tsx @@ -158,13 +158,19 @@ function PlayerGameView(props: PlayerGameViewProps) { const { currentUser, game } = useAppSelector((state) => state); const [copiedEmail, setCopiedEmail] = useState(false); - const [submission, setSubmission] = useState(null); + const [submissions, setSubmissions] = useState([]); + + const [problems, setProblems] = useState([]); // todo: change to game.problems + const [languageList, setLanguageList] = useState([Language.Java]); + const [codeList, setCodeList] = useState(['']); + const [currentSubmission, setCurrentSubmission] = useState(null); + const [currentProblemIndex, setCurrentProblemIndex] = useState(0); const [loading, setLoading] = useState(false); const [error, setError] = useState(gameError); - const [currentLanguage, setCurrentLanguage] = useState(Language.Java); - const [currentCode, setCurrentCode] = useState(''); + const [currentLanguage, setCurrentLanguage] = useState(Language.Java); // todo: replace with languagelist + const [currentCode, setCurrentCode] = useState(''); // todo: replace with codelist const [defaultCodeList, setDefaultCodeList] = useState([]); // Variable to hold whether the user is subscribed to their own player socket. @@ -182,6 +188,39 @@ function PlayerGameView(props: PlayerGameViewProps) { */ useBeforeunload(() => 'Leaving this page may cause you to lose your current code and data.'); + const createCodeLanguageArray = () => { + while (languageList.length < problems.length) { + languageList.push(Language.Java); + } + + while (codeList.length < problems.length) { + codeList.push(''); + } + }; + + // todo: move into useEffect + createCodeLanguageArray(); + + // todo: no directly modifying state + const setOneCurrentLanguage = (newLanguage: Language) => { + languageList[currentProblemIndex] = newLanguage; + }; + + const setOneCurrentCode = (newCode: string) => { + codeList[currentProblemIndex] = newCode; + }; + + // Returns the most recent submission made for problem of index curr. + const getSubmission = (curr: number, playerSubmissions: Submission[]) => { + for (let i = playerSubmissions.length - 1; i >= 0; i -= 1) { + if (playerSubmissions[i].problemIndex === curr) { + return playerSubmissions[i]; + } + } + + return null; + }; + // References necessary for the spectator subscription callback. const stateRef = useRef(); stateRef.current = { @@ -192,7 +231,9 @@ function PlayerGameView(props: PlayerGameViewProps) { }; const setDefaultCodeFromProblems = useCallback((problemsParam: Problem[], - code: string, language: Language) => { + playerSubmissions: Submission[]) => { + setSubmissions(playerSubmissions); + const promises: Promise[] = []; problemsParam.forEach((problem) => { if (problem && problem.problemId) { @@ -202,22 +243,36 @@ function PlayerGameView(props: PlayerGameViewProps) { // Get the result of promises and set the default code list. Promise.all(promises).then((result) => { - const codeMap = result[0]; - - // If previous code and language specified, save those as defaults - if (code) { - codeMap[language] = code; + const newCodeList = []; + const newLanguageList = []; + const codeMap = result; + + // Save the default code in these temporary lists + for (let i = 0; i < result.length; i += 1) { + newCodeList.push(result[i][Language.Java]); + newLanguageList.push(Language.Java); } - // Set this user's current code and language - setCurrentCode(codeMap[language]); - setCurrentLanguage(language); + // If previous code and language specified, override the defaults + for (let i = 0; i < result.length; i += 1) { + const temp = getSubmission(i, playerSubmissions); + + if (temp) { + newCodeList[i] = temp.code; + codeMap[i][temp.language as Language] = temp.code; + newLanguageList[i] = temp.language as Language; + setCurrentProblemIndex(i); + } + } - setDefaultCodeList(result); + // Set this user's current code + setCodeList(newCodeList); + setLanguageList(newLanguageList); + setDefaultCodeList(codeMap); }).catch((err) => { setError(err.message); }); - }, [setDefaultCodeList, setCurrentCode, setCurrentLanguage]); + }, [setDefaultCodeList, setCodeList, setLanguageList]); const sendViewUpdate = useCallback((gameParam: Game | null | undefined, currentUserParam: User | null | undefined, @@ -293,15 +348,15 @@ function PlayerGameView(props: PlayerGameViewProps) { // If this user refreshed and has already submitted code, load and save their latest code game.players.forEach((player) => { - if (player.user.userId === currentUser?.userId && player.code) { - setDefaultCodeFromProblems(game.problems, player.code, player.language as Language); + if (player.user.userId === currentUser?.userId && player.submissions) { + setDefaultCodeFromProblems(game.problems, player.submissions); matchFound = true; } }); // If no previous code, proceed as normal with the default Java language if (!matchFound) { - setDefaultCodeFromProblems(game.problems, '', Language.Java); + setDefaultCodeFromProblems(game.problems, []); } } } @@ -321,8 +376,9 @@ function PlayerGameView(props: PlayerGameViewProps) { const request = { initiator: currentUser!, input, - code: currentCode, - language: currentLanguage, + code: codeList[currentProblemIndex], + language: languageList[currentProblemIndex], + problemIndex: currentProblemIndex, }; runSolution(game!.room.roomId, request) @@ -332,7 +388,7 @@ function PlayerGameView(props: PlayerGameViewProps) { // Set the 'test' submission type to correctly display result. // eslint-disable-next-line no-param-reassign res.submissionType = SubmissionType.Test; - setSubmission(res); + setCurrentSubmission(res); }) .catch((err) => { setLoading(false); @@ -346,8 +402,9 @@ function PlayerGameView(props: PlayerGameViewProps) { setError(''); const request = { initiator: currentUser!, - code: currentCode, - language: currentLanguage, + code: codeList[currentProblemIndex], + language: languageList[currentProblemIndex], + problemIndex: currentProblemIndex, }; submitSolution(game!.room.roomId, request) @@ -357,7 +414,8 @@ function PlayerGameView(props: PlayerGameViewProps) { // Set the 'submit' submission type to correctly display result. // eslint-disable-next-line no-param-reassign res.submissionType = SubmissionType.Submit; - setSubmission(res); + setSubmissions(submissions.concat([res])); + setCurrentSubmission(res); }) .catch((err) => { setLoading(false); @@ -365,14 +423,32 @@ function PlayerGameView(props: PlayerGameViewProps) { }); }; + // todo: no wrap + const nextProblem = () => { + setCurrentProblemIndex((currentProblemIndex + 1) % problems?.length); + setCurrentSubmission(getSubmission((currentProblemIndex + 1) % problems?.length, submissions)); + }; + + const previousProblem = () => { + let temp = currentProblemIndex - 1; + + if (temp < 0) { + temp += problems?.length; + } + + setCurrentProblemIndex(temp); + setCurrentSubmission(getSubmission(temp, submissions)); + }; + const displayPlayerLeaderboard = useCallback(() => game?.players.map((player, index) => ( - )), [game, currentUser]); + )), [game, currentUser, problems.length]); return ( <> @@ -433,14 +509,13 @@ function PlayerGameView(props: PlayerGameViewProps) { {/* Problem title/description panel */} - {!spectateGame ? game?.problems[0]?.name : spectateGame?.problem.name} + {!spectateGame ? game?.problems[currentProblemIndex]?.name : spectateGame?.problem.name} - {/* TODO: I don't know whether we have to verify that the problem exists. */} { !spectateGame ? ( - getDifficultyDisplayButton(game?.problems[0].difficulty!) + getDifficultyDisplayButton(game?.problems[currentProblemIndex].difficulty!) ) : ( - getDifficultyDisplayButton(spectateGame?.problem.difficulty!) + getDifficultyDisplayButton(spectateGame?.problem.difficulty!) // todo: change problems ) } ''} readOnly /> @@ -478,18 +553,20 @@ function PlayerGameView(props: PlayerGameViewProps) { > languageList[currentProblemIndex]} + defaultCodeMap={defaultCodeList} + currentProblem={currentProblemIndex} + defaultLanguage={Language.Java} defaultCode={null} liveCode={null} /> @@ -500,8 +577,10 @@ function PlayerGameView(props: PlayerGameViewProps) { spectateGame?.language as Language} // todo modified: is this right? + defaultCodeMap={null} // todo: verify this is right + currentProblem={currentProblemIndex} // todo: needed defaultCode={spectateGame?.code} liveCode={spectateGame?.code} /> diff --git a/frontend/src/views/Game.tsx b/frontend/src/views/Game.tsx index b34096403..39b371c69 100644 --- a/frontend/src/views/Game.tsx +++ b/frontend/src/views/Game.tsx @@ -17,9 +17,7 @@ import { Difficulty } from '../api/Difficulty'; import { Game, manuallyEndGame } from '../api/Game'; import GameTimerContainer from '../components/game/GameTimerContainer'; import { GameTimer } from '../api/GameTimer'; -import { - TextButton, DifficultyDisplayButton, SmallButton, DangerButton, -} from '../components/core/Button'; +import { TextButton, DangerButton } from '../components/core/Button'; import { connect, routes, subscribe, } from '../api/Socket'; @@ -45,10 +43,6 @@ function GamePage() { const history = useHistory(); const location = useLocation(); - // todo: possibly delete - const [copiedEmail, setCopiedEmail] = useState(false); - const [submissions, setSubmissions] = useState([]); - const [roomId, setRoomId] = useState(''); const [fullPageLoading, setFullPageLoading] = useState(true); @@ -58,13 +52,6 @@ function GamePage() { const [spectators, setSpectators] = useState([]); const [gameTimer, setGameTimer] = useState(null); - // todo: move block to new file - const [problems, setProblems] = useState([]); - const [languageList, setLanguageList] = useState([Language.Java]); - const [codeList, setCodeList] = useState(['']); - const [currentSubmission, setCurrentSubmission] = useState(null); - const [currentProblemIndex, setCurrentProblemIndex] = useState(0); - const [timeUp, setTimeUp] = useState(false); const [allSolved, setAllSolved] = useState(false); const [gameEnded, setGameEnded] = useState(false); @@ -93,109 +80,14 @@ function GamePage() { setSpectators(newGame.room.spectators); }; - // todo: move - const createCodeLanguageArray = () => { - while (languageList.length < problems.length) { - languageList.push(Language.Java); - } - - while (codeList.length < problems.length) { - codeList.push(''); - } - }; - const dispatch = useAppDispatch(); const { currentUser, game } = useAppSelector((state) => state); - // todo: move to useEffect - createCodeLanguageArray(); - - // todo: all of this and setDefaultCode, migrate to file - const setOneCurrentLanguage = (newLanguage: Language) => { - languageList[currentProblemIndex] = newLanguage; - }; - - const setOneCurrentCode = (newCode: string) => { - codeList[currentProblemIndex] = newCode; - }; - - // Returns the most recent submission made for problem of index curr. - const getSubmission = (curr: number, playerSubmissions: Submission[]) => { - for (let i = playerSubmissions.length - 1; i >= 0; i -= 1) { - if (playerSubmissions[i].problemIndex === curr) { - return playerSubmissions[i]; - } - } - - return null; - }; - - const setDefaultCodeFromProblems = useCallback((problemsParam: Problem[], - playerSubmissions: Submission[]) => { - setSubmissions(playerSubmissions); - const promises: Promise[] = []; - problemsParam.forEach((problem) => { - if (problem && problem.problemId) { - promises.push(getDefaultCodeMap(problem.problemId)); - } - }); - - // Get the result of promises and set the default code list. - Promise.all(promises).then((result) => { - const newCodeList = []; - const newLanguageList = []; - const codeMap = result; - - for (let i = 0; i < result.length; i += 1) { - newCodeList.push(result[i][Language.Java]); - newLanguageList.push(Language.Java); - } - - // If previous code and language specified, save those as defaults - for (let i = 0; i < result.length; i += 1) { - const temp = getSubmission(i, playerSubmissions); - - if (temp != null) { - newCodeList[i] = temp.code; - codeMap[i][temp.language as Language] = temp.code; - newLanguageList[i] = temp.language as Language; - setCurrentProblemIndex(i); - } - } - - // Set this user's current code - setCodeList(newCodeList); - setLanguageList(newLanguageList); - setDefaultCodeList(codeMap); - }).catch((err) => { - setError(err.message); - }); - }, [setDefaultCodeList, setCodeList, setLanguageList]); - // Map the game in Redux to the state variables used in this file useEffect(() => { if (game) { setFullPageLoading(false); setStateFromGame(game); - - // todo: remaining part of block, migrate - // If default code list is empty and current user is loaded, fetch the code from the backend - if (!defaultCodeList.length && currentUser) { - let matchFound = false; - - // If this user refreshed and has already submitted code, load and save their latest code - game.players.forEach((player) => { - if (player.user.userId === currentUser?.userId && player.submissions) { - setDefaultCodeFromProblems(game.problems, player.submissions); - matchFound = true; - } - }); - - // If no previous code, proceed as normal with the default Java language - if (!matchFound) { - setDefaultCodeFromProblems(game.problems, []); - } - } } }, [game, setFullPageLoading]); @@ -265,26 +157,8 @@ function GamePage() { error: errorHandler('No valid room details were provided, so you could not view the game page.'), }); } - // todo: the block that was here (now deleted), copy that over }, [game, currentUser, dispatch, location, history]); - // todo: copy over - const nextProblem = () => { - setCurrentProblemIndex((currentProblemIndex + 1) % problems?.length); - setCurrentSubmission(getSubmission((currentProblemIndex + 1) % problems?.length, submissions)); - }; - - const previousProblem = () => { - let temp = currentProblemIndex - 1; - - if (temp < 0) { - temp += problems?.length; - } - - setCurrentProblemIndex(temp); - setCurrentSubmission(getSubmission(temp, submissions)); - }; - const endGameAction = () => { // eslint-disable-next-line no-alert if (!window.confirm('Are you sure you want to end the game for all players?')) { @@ -295,17 +169,6 @@ function GamePage() { .catch((err) => setError(err.message)); }; - // todo: copy this method over - const displayPlayerLeaderboard = useCallback(() => players.map((player, index) => ( - - )), [players, currentUser, problems.length]); - // Subscribe user to primary socket and to notifications. useEffect(() => { if (!gameSocket && roomId && currentUser?.userId) { @@ -349,7 +212,7 @@ function GamePage() { ) : null} - { // todo: this entire block that was deleted, copy over + { currentUser?.spectator ? ( ) : ( From 14825f8112dbc79790c252f10156bc4ff1556121 Mon Sep 17 00:00:00 2001 From: Alan Bi Date: Tue, 15 Jun 2021 22:21:46 -0700 Subject: [PATCH 28/37] Get compiling and running with bugs --- frontend/src/components/game/Console.tsx | 2 +- .../src/components/game/PlayerGameView.tsx | 22 ++++++++++++------- .../src/components/game/SpectatorGameView.tsx | 1 + .../components/results/PreviewCodeContent.tsx | 4 +++- frontend/src/views/Lobby.tsx | 14 +----------- 5 files changed, 20 insertions(+), 23 deletions(-) diff --git a/frontend/src/components/game/Console.tsx b/frontend/src/components/game/Console.tsx index 5358460ad..5195658d3 100644 --- a/frontend/src/components/game/Console.tsx +++ b/frontend/src/components/game/Console.tsx @@ -130,7 +130,7 @@ function Console(props: ConsoleProps) { // Default values setTitle('Console'); setSubtitle(''); - setInput(testCases[0].input); + setInput(testCases ? testCases[0].input : ''); setOutput(''); setConsoleOutput(''); } diff --git a/frontend/src/components/game/PlayerGameView.tsx b/frontend/src/components/game/PlayerGameView.tsx index 2585bde3d..befc69c18 100644 --- a/frontend/src/components/game/PlayerGameView.tsx +++ b/frontend/src/components/game/PlayerGameView.tsx @@ -33,7 +33,7 @@ import { Player, } from '../../api/Game'; import LeaderboardCard from '../card/LeaderboardCard'; -import { getDifficultyDisplayButton, InheritedTextButton } from '../core/Button'; +import { getDifficultyDisplayButton, InheritedTextButton, SmallButton } from '../core/Button'; import { SpectatorBackIcon } from '../core/Icon'; import Language from '../../api/Language'; import { CopyIndicator, BottomCopyIndicatorContainer, InlineCopyIcon } from '../special/CopyIndicator'; @@ -160,7 +160,7 @@ function PlayerGameView(props: PlayerGameViewProps) { const [copiedEmail, setCopiedEmail] = useState(false); const [submissions, setSubmissions] = useState([]); - const [problems, setProblems] = useState([]); // todo: change to game.problems + const [problems, setProblems] = useState([]); const [languageList, setLanguageList] = useState([Language.Java]); const [codeList, setCodeList] = useState(['']); const [currentSubmission, setCurrentSubmission] = useState(null); @@ -169,8 +169,6 @@ function PlayerGameView(props: PlayerGameViewProps) { const [loading, setLoading] = useState(false); const [error, setError] = useState(gameError); - const [currentLanguage, setCurrentLanguage] = useState(Language.Java); // todo: replace with languagelist - const [currentCode, setCurrentCode] = useState(''); // todo: replace with codelist const [defaultCodeList, setDefaultCodeList] = useState([]); // Variable to hold whether the user is subscribed to their own player socket. @@ -180,6 +178,10 @@ function PlayerGameView(props: PlayerGameViewProps) { const [spectatedPlayer, setSpectatedPlayer] = useState(null); const bestSubmission = useBestSubmission(spectatedPlayer); + useEffect(() => setProblems(game?.problems || []), [game]); + console.log(game); + console.log(problems); + /** * Display beforeUnload message to inform the user that they may lose * their code / data if they leave the page. @@ -226,8 +228,8 @@ function PlayerGameView(props: PlayerGameViewProps) { stateRef.current = { game, currentUser, - currentCode, - currentLanguage, + currentCode: codeList[currentProblemIndex], + currentLanguage: languageList[currentProblemIndex], }; const setDefaultCodeFromProblems = useCallback((problemsParam: Problem[], @@ -295,8 +297,8 @@ function PlayerGameView(props: PlayerGameViewProps) { // Send updates via socket to any spectators. useEffect(() => { - sendViewUpdate(game, currentUser, currentCode, currentLanguage); - }, [game, currentUser, currentCode, currentLanguage, sendViewUpdate]); + sendViewUpdate(game, currentUser, codeList[currentProblemIndex], languageList[currentProblemIndex]); + }, [game, currentUser, codeList, languageList, currentProblemIndex, sendViewUpdate]); // Re-subscribe in order to get the correct subscription callback. const subscribePlayer = useCallback((roomIdParam: string, userIdParam: string) => { @@ -589,6 +591,10 @@ function PlayerGameView(props: PlayerGameViewProps) { } + + Previous + Next + setCopiedEmail(false)}> Email copied!  ✕ diff --git a/frontend/src/components/game/SpectatorGameView.tsx b/frontend/src/components/game/SpectatorGameView.tsx index 1b65c695a..7e3f8f7c9 100644 --- a/frontend/src/components/game/SpectatorGameView.tsx +++ b/frontend/src/components/game/SpectatorGameView.tsx @@ -83,6 +83,7 @@ function SpectatorGameView() { subscribePlayer(game.room.roomId, game.players[index].user.userId!); } }} + numProblems={game?.problems.length || 1} /> {error ? : null} {loading ? : null} diff --git a/frontend/src/components/results/PreviewCodeContent.tsx b/frontend/src/components/results/PreviewCodeContent.tsx index 4cbbb94d9..c1371d9a8 100644 --- a/frontend/src/components/results/PreviewCodeContent.tsx +++ b/frontend/src/components/results/PreviewCodeContent.tsx @@ -45,7 +45,9 @@ function PreviewCodeContent(props: PreviewCodeContentProps) { - { - if (!isHost(currentUser)) { - setHoverVisible(true); - } - }} - onMouseLeave={() => { - if (!isHost(currentUser)) { - setHoverVisible(false); - } - }} - /> + Date: Wed, 16 Jun 2021 21:42:43 -0700 Subject: [PATCH 29/37] Get spectating and overall game working without major bugs --- .../src/components/game/PlayerGameView.tsx | 54 ++++++++++++------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/frontend/src/components/game/PlayerGameView.tsx b/frontend/src/components/game/PlayerGameView.tsx index befc69c18..79f589e0f 100644 --- a/frontend/src/components/game/PlayerGameView.tsx +++ b/frontend/src/components/game/PlayerGameView.tsx @@ -135,6 +135,7 @@ type StateRefType = { currentUser: User | null, currentCode: string, currentLanguage: string, + currentIndex: number, } /** @@ -179,8 +180,6 @@ function PlayerGameView(props: PlayerGameViewProps) { const bestSubmission = useBestSubmission(spectatedPlayer); useEffect(() => setProblems(game?.problems || []), [game]); - console.log(game); - console.log(problems); /** * Display beforeUnload message to inform the user that they may lose @@ -190,26 +189,37 @@ function PlayerGameView(props: PlayerGameViewProps) { */ useBeforeunload(() => 'Leaving this page may cause you to lose your current code and data.'); - const createCodeLanguageArray = () => { - while (languageList.length < problems.length) { - languageList.push(Language.Java); - } + // const createCodeLanguageArray = () => { + // while (languageList.length < problems.length) { + // languageList.push(Language.Java); + // } + // + // while (codeList.length < problems.length) { + // codeList.push(''); + // } + // }; - while (codeList.length < problems.length) { - codeList.push(''); - } - }; + // useEffect(() => createCodeLanguageArray(), []); - // todo: move into useEffect - createCodeLanguageArray(); + const getCurrentLanguage = useCallback(() => languageList[currentProblemIndex], + [languageList, currentProblemIndex]); - // todo: no directly modifying state const setOneCurrentLanguage = (newLanguage: Language) => { - languageList[currentProblemIndex] = newLanguage; + setLanguageList(languageList.map((current, index) => { + if (index === currentProblemIndex) { + return newLanguage; + } + return current; + })); }; const setOneCurrentCode = (newCode: string) => { - codeList[currentProblemIndex] = newCode; + setCodeList(codeList.map((current, index) => { + if (index === currentProblemIndex) { + return newCode; + } + return current; + })); }; // Returns the most recent submission made for problem of index curr. @@ -230,6 +240,7 @@ function PlayerGameView(props: PlayerGameViewProps) { currentUser, currentCode: codeList[currentProblemIndex], currentLanguage: languageList[currentProblemIndex], + currentIndex: currentProblemIndex, }; const setDefaultCodeFromProblems = useCallback((problemsParam: Problem[], @@ -279,11 +290,12 @@ function PlayerGameView(props: PlayerGameViewProps) { const sendViewUpdate = useCallback((gameParam: Game | null | undefined, currentUserParam: User | null | undefined, currentCodeParam: string | undefined, - currentLanguageParam: string | undefined) => { + currentLanguageParam: string | undefined, + currentIndexParam: number | undefined) => { if (gameParam && currentUserParam) { const spectatorViewBody: string = JSON.stringify({ user: currentUserParam, - problem: gameParam.problems[0], + problem: gameParam.problems[currentIndexParam || 0], // must satisfy problems.length > 0 code: currentCodeParam, language: currentLanguageParam, }); @@ -297,7 +309,8 @@ function PlayerGameView(props: PlayerGameViewProps) { // Send updates via socket to any spectators. useEffect(() => { - sendViewUpdate(game, currentUser, codeList[currentProblemIndex], languageList[currentProblemIndex]); + sendViewUpdate(game, currentUser, codeList[currentProblemIndex], + languageList[currentProblemIndex], currentProblemIndex); }, [game, currentUser, codeList, languageList, currentProblemIndex, sendViewUpdate]); // Re-subscribe in order to get the correct subscription callback. @@ -306,7 +319,8 @@ function PlayerGameView(props: PlayerGameViewProps) { const subscribePlayerCallback = (result: Message) => { if (JSON.parse(result.body).newSpectator) { sendViewUpdate(stateRef.current?.game, stateRef.current?.currentUser, - stateRef.current?.currentCode, stateRef.current?.currentLanguage); + stateRef.current?.currentCode, stateRef.current?.currentLanguage, + stateRef.current?.currentIndex); } }; @@ -557,7 +571,7 @@ function PlayerGameView(props: PlayerGameViewProps) { languageList[currentProblemIndex]} + getCurrentLanguage={getCurrentLanguage} defaultCodeMap={defaultCodeList} currentProblem={currentProblemIndex} defaultLanguage={Language.Java} From 12d35a64c43ec471fd6c64fcf96378a853b79428 Mon Sep 17 00:00:00 2001 From: Alan Bi Date: Thu, 17 Jun 2021 20:59:13 -0700 Subject: [PATCH 30/37] Implement score and time methods in backend mapper --- .../codejoust/main/dto/game/GameMapper.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/main/java/com/codejoust/main/dto/game/GameMapper.java b/src/main/java/com/codejoust/main/dto/game/GameMapper.java index b5ecc7781..6eaf6d780 100644 --- a/src/main/java/com/codejoust/main/dto/game/GameMapper.java +++ b/src/main/java/com/codejoust/main/dto/game/GameMapper.java @@ -3,10 +3,13 @@ import org.modelmapper.ModelMapper; import org.modelmapper.convention.MatchingStrategies; +import java.time.Instant; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import com.codejoust.main.dto.problem.ProblemDto; import com.codejoust.main.dto.problem.ProblemMapper; @@ -128,4 +131,32 @@ public static void sortLeaderboard(List players) { return bestSub2.getNumCorrect() - bestSub1.getNumCorrect(); }); } + + // Get total number of problems solved + private int getScore(List submissions) { + Set set = new HashSet<>(); + for (SubmissionDto submission : submissions) { + if (submission.getNumCorrect().equals(submission.getNumTestCases())) { + set.add(submission.getProblemIndex()); + } + } + + return set.size(); + } + + // Get time of latest correct solution, or null if none exists + private Instant getTime(List submissions) { + Set set = new HashSet<>(); + Instant instant = null; + + for (SubmissionDto submission : submissions) { + if (submission.getNumCorrect().equals(submission.getNumTestCases()) + && !set.contains(submission.getProblemIndex())) { + set.add(submission.getProblemIndex()); + instant = submission.getStartTime(); + } + } + + return instant; + } } From ba3f9c1a6b04c13355d308541c9e834f46623d8d Mon Sep 17 00:00:00 2001 From: Alan Bi Date: Thu, 17 Jun 2021 21:20:25 -0700 Subject: [PATCH 31/37] Fix bug with incorrectly sorted leaderboard --- .../codejoust/main/dto/game/GameMapper.java | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/codejoust/main/dto/game/GameMapper.java b/src/main/java/com/codejoust/main/dto/game/GameMapper.java index 6eaf6d780..4925a471b 100644 --- a/src/main/java/com/codejoust/main/dto/game/GameMapper.java +++ b/src/main/java/com/codejoust/main/dto/game/GameMapper.java @@ -106,34 +106,29 @@ public static void sortLeaderboard(List players) { return -1; } - SubmissionDto bestSub1 = submissions1.get(0); - SubmissionDto bestSub2 = submissions2.get(0); + int score1 = getScore(submissions1); + int score2 = getScore(submissions2); - // Get the best solution by each player (highest score, then earliest submission) - for (SubmissionDto sub : submissions1) { - if (sub.getNumCorrect() > bestSub1.getNumCorrect()) { - bestSub1 = sub; - } - } + // If both have the same numCorrect, whoever submits earlier is first + if (score1 == score2) { + Instant time1 = getTime(submissions1); + Instant time2 = getTime(submissions2); - for (SubmissionDto sub : submissions2) { - if (sub.getNumCorrect() > bestSub2.getNumCorrect()) { - bestSub2 = sub; + // If neither has submitted correctly, oh well (if one is null, the other must be as well) + if (time1 == null || time2 == null) { + return 0; } - } - // If both have the same numCorrect, whoever submits earlier is first - if (bestSub1.getNumCorrect().equals(bestSub2.getNumCorrect())) { - return bestSub1.getStartTime().compareTo(bestSub2.getStartTime()); + return time1.compareTo(time2); } // Whoever has higher numCorrect is first - return bestSub2.getNumCorrect() - bestSub1.getNumCorrect(); + return score2 - score1; }); } // Get total number of problems solved - private int getScore(List submissions) { + private static int getScore(List submissions) { Set set = new HashSet<>(); for (SubmissionDto submission : submissions) { if (submission.getNumCorrect().equals(submission.getNumTestCases())) { @@ -145,7 +140,7 @@ private int getScore(List submissions) { } // Get time of latest correct solution, or null if none exists - private Instant getTime(List submissions) { + private static Instant getTime(List submissions) { Set set = new HashSet<>(); Instant instant = null; From ffb571f7bb7d57bfe2dca995986118ac63c29009 Mon Sep 17 00:00:00 2001 From: Alan Bi Date: Thu, 17 Jun 2021 21:47:42 -0700 Subject: [PATCH 32/37] Fix broken leaderboard tests --- .../codejoust/main/dto/game/PlayerDto.java | 3 +- .../main/mapper/GameMapperTests.java | 38 +++++++++++-------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/codejoust/main/dto/game/PlayerDto.java b/src/main/java/com/codejoust/main/dto/game/PlayerDto.java index 474d2acb9..6b631ad07 100644 --- a/src/main/java/com/codejoust/main/dto/game/PlayerDto.java +++ b/src/main/java/com/codejoust/main/dto/game/PlayerDto.java @@ -13,8 +13,9 @@ @Getter @Setter -@EqualsAndHashCode +@EqualsAndHashCode(onlyExplicitlyIncluded = true) public class PlayerDto { + @EqualsAndHashCode.Include private UserDto user; private String code; private CodeLanguage language; diff --git a/src/test/java/com/codejoust/main/mapper/GameMapperTests.java b/src/test/java/com/codejoust/main/mapper/GameMapperTests.java index 2c6bb4043..6081578eb 100644 --- a/src/test/java/com/codejoust/main/mapper/GameMapperTests.java +++ b/src/test/java/com/codejoust/main/mapper/GameMapperTests.java @@ -35,9 +35,10 @@ public class GameMapperTests { private static final int TEST_CASES = 10; // Helper method to add a dummy submission to a PlayerDto object - private void addSubmissionHelper(PlayerDto playerDto, int numCorrect) { + private void addSubmissionHelper(PlayerDto playerDto, int numCorrect, int problemIndex) { SubmissionDto submissionDto = new SubmissionDto(); submissionDto.setNumCorrect(numCorrect); + submissionDto.setProblemIndex(problemIndex); submissionDto.setStartTime(Instant.now()); playerDto.getSubmissions().add(submissionDto); @@ -158,20 +159,23 @@ public void sortLeaderboardSuccess() { // Note: order of addSubmissionHelper matters (time of submission) PlayerDto player1 = new PlayerDto(); - addSubmissionHelper(player1, 0); + addSubmissionHelper(player1, TEST_CASES, 0); + addSubmissionHelper(player1, TEST_CASES, 0); + addSubmissionHelper(player1, TEST_CASES, 0); PlayerDto player2 = new PlayerDto(); - addSubmissionHelper(player2, 0); - addSubmissionHelper(player2, 3); + addSubmissionHelper(player2, TEST_CASES, 0); + addSubmissionHelper(player2, TEST_CASES, 1); PlayerDto player3 = new PlayerDto(); - addSubmissionHelper(player3, 3); - - addSubmissionHelper(player2, 3); + addSubmissionHelper(player3, 0, 0); PlayerDto player4 = new PlayerDto(); - addSubmissionHelper(player4, 5); - addSubmissionHelper(player4, 1); + addSubmissionHelper(player4, TEST_CASES, 0); + addSubmissionHelper(player4, TEST_CASES, 1); + + // Player 2 submits wrong afterwards, but it doesn't count against his time + addSubmissionHelper(player2, 0, 1); PlayerDto player5 = new PlayerDto(); @@ -181,13 +185,13 @@ public void sortLeaderboardSuccess() { players.add(player4); players.add(player5); - // Player order should be: [4, 2, 3, 1, 5] + // Player order should be: [2, 4, 1, 3, 5] GameMapper.sortLeaderboard(players); - assertEquals(player4, players.get(0)); - assertEquals(player2, players.get(1)); - assertEquals(player3, players.get(2)); - assertEquals(player1, players.get(3)); + assertEquals(player2, players.get(0)); + assertEquals(player4, players.get(1)); + assertEquals(player1, players.get(2)); + assertEquals(player3, players.get(3)); assertEquals(player5, players.get(4)); } @@ -195,8 +199,10 @@ public void sortLeaderboardSuccess() { public void toDtoSortsLeaderboard() { Submission sub1 = new Submission(); sub1.setNumCorrect(0); + sub1.setNumTestCases(TEST_CASES); Submission sub2 = new Submission(); - sub2.setNumCorrect(1); + sub2.setNumCorrect(TEST_CASES); + sub2.setNumTestCases(TEST_CASES); Player player1 = new Player(); player1.getSubmissions().add(sub1); @@ -212,7 +218,7 @@ public void toDtoSortsLeaderboard() { List players = gameDto.getPlayers(); assertEquals(2, players.size()); - assertEquals(1, players.get(0).getSubmissions().get(0).getNumCorrect()); + assertEquals(TEST_CASES, players.get(0).getSubmissions().get(0).getNumCorrect()); assertEquals(0, players.get(1).getSubmissions().get(0).getNumCorrect()); } } From 14c33c3158be2a262fd7ae28b549b87130a02111 Mon Sep 17 00:00:00 2001 From: Alan Bi Date: Sun, 20 Jun 2021 16:06:55 -0700 Subject: [PATCH 33/37] Clean up code for merging --- .github/workflows/workflow.yml | 2 +- .../src/components/game/PlayerGameView.tsx | 29 +++++++------------ .../components/results/PreviewCodeContent.tsx | 2 +- frontend/src/util/Hook.tsx | 4 +-- src/main/resources/application.properties | 2 +- 5 files changed, 15 insertions(+), 24 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 82e64837b..43dd2863d 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -2,7 +2,7 @@ name: Java CI/CD on: push: - branches: [ main, stagingNumProblems ] + branches: [ main ] pull_request: branches: [ main ] diff --git a/frontend/src/components/game/PlayerGameView.tsx b/frontend/src/components/game/PlayerGameView.tsx index 79f589e0f..d6850fe11 100644 --- a/frontend/src/components/game/PlayerGameView.tsx +++ b/frontend/src/components/game/PlayerGameView.tsx @@ -189,18 +189,6 @@ function PlayerGameView(props: PlayerGameViewProps) { */ useBeforeunload(() => 'Leaving this page may cause you to lose your current code and data.'); - // const createCodeLanguageArray = () => { - // while (languageList.length < problems.length) { - // languageList.push(Language.Java); - // } - // - // while (codeList.length < problems.length) { - // codeList.push(''); - // } - // }; - - // useEffect(() => createCodeLanguageArray(), []); - const getCurrentLanguage = useCallback(() => languageList[currentProblemIndex], [languageList, currentProblemIndex]); @@ -439,7 +427,6 @@ function PlayerGameView(props: PlayerGameViewProps) { }); }; - // todo: no wrap const nextProblem = () => { setCurrentProblemIndex((currentProblemIndex + 1) % problems?.length); setCurrentSubmission(getSubmission((currentProblemIndex + 1) % problems?.length, submissions)); @@ -525,13 +512,15 @@ function PlayerGameView(props: PlayerGameViewProps) { {/* Problem title/description panel */} - {!spectateGame ? game?.problems[currentProblemIndex]?.name : spectateGame?.problem.name} + {!spectateGame + ? game?.problems[currentProblemIndex]?.name + : spectateGame?.problem.name} { !spectateGame ? ( getDifficultyDisplayButton(game?.problems[currentProblemIndex].difficulty!) ) : ( - getDifficultyDisplayButton(spectateGame?.problem.difficulty!) // todo: change problems + getDifficultyDisplayButton(spectateGame?.problem.difficulty!) ) } ''} readOnly /> @@ -594,9 +585,9 @@ function PlayerGameView(props: PlayerGameViewProps) { onLanguageChange={null} onCodeChange={null} defaultLanguage={spectateGame?.language as Language} - getCurrentLanguage={() => spectateGame?.language as Language} // todo modified: is this right? - defaultCodeMap={null} // todo: verify this is right - currentProblem={currentProblemIndex} // todo: needed + getCurrentLanguage={() => spectateGame?.language as Language} + defaultCodeMap={null} + currentProblem={currentProblemIndex} defaultCode={spectateGame?.code} liveCode={spectateGame?.code} /> diff --git a/frontend/src/components/results/PreviewCodeContent.tsx b/frontend/src/components/results/PreviewCodeContent.tsx index c1371d9a8..f823d1903 100644 --- a/frontend/src/components/results/PreviewCodeContent.tsx +++ b/frontend/src/components/results/PreviewCodeContent.tsx @@ -47,7 +47,7 @@ function PreviewCodeContent(props: PreviewCodeContentProps) { onCodeChange={null} getCurrentLanguage={null} defaultCodeMap={null} - currentProblem={0} // todo: verify irrelevant + currentProblem={0} defaultLanguage={bestSubmission?.language as Language || Language.Java} defaultCode={bestSubmission?.code || 'Uh oh! An error occurred fetching this player\'s code'} liveCode={null} diff --git a/frontend/src/util/Hook.tsx b/frontend/src/util/Hook.tsx index ac2cea8bc..5954c3414 100644 --- a/frontend/src/util/Hook.tsx +++ b/frontend/src/util/Hook.tsx @@ -45,7 +45,7 @@ export const useGetScore = (player?: Player) => { setScore(counted.size); } - }, [player, setScore]); + }, [player, setScore, counted]); if (player == null || player.submissions.length === 0) { return null; @@ -67,7 +67,7 @@ export const useGetSubmissionTime = (player?: Player) => { } } } - }, [player]); + }, [player, counted]); if (!time && player && player.submissions.length > 0) { setTime(player.submissions[player.submissions.length - 1].startTime); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 4464daa44..63665d7c8 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -15,7 +15,7 @@ spring.datasource.hikari.maximumPoolSize=10 # Whether to return dummy submission when tester service is unavailable tester.debugMode=false -tester.url=http://35.238.172.126:8080/api/v1/runner +tester.url=http://35.222.3.75:8080/api/v1/runner # Whether to mock firebase service for testing purposes firebase.debugMode=false From 8c238fde3add9bb360067c219f4cbb269294c169 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Sat, 26 Jun 2021 19:32:18 -0700 Subject: [PATCH 34/37] responding to first half of the nits --- frontend/src/components/game/Editor.tsx | 10 ++++++ .../src/components/game/PlayerGameView.tsx | 36 ++++--------------- .../components/results/PlayerResultsItem.tsx | 29 +++------------ frontend/src/util/Hook.tsx | 16 +++++++++ frontend/src/util/Utility.ts | 10 +++++- frontend/src/views/Lobby.tsx | 4 +-- .../codejoust/main/service/SubmitService.java | 1 - 7 files changed, 47 insertions(+), 59 deletions(-) diff --git a/frontend/src/components/game/Editor.tsx b/frontend/src/components/game/Editor.tsx index 7a8ff31a2..7efb75ba0 100644 --- a/frontend/src/components/game/Editor.tsx +++ b/frontend/src/components/game/Editor.tsx @@ -5,6 +5,16 @@ import styled, { ThemeContext } from 'styled-components'; import Language, { fromString, languageToEditorLanguage } from '../../api/Language'; import { DefaultCodeType } from '../../api/Problem'; +/** + * onLanguageChange - a callback, called when the currentLanguage changes + * onCodeChange - a callback, called when the code changes + * getCurrentLanguage - a function passed in by the PlayerGameView which can be called to get the + * working language + * defaultCodeMap - a map of all the default code for each problem and language + * defaultLanguage - the default language for the editor + * currentProblem - the problem which is current being worked on + * liveCode - ???? + */ type EditorProps = { onLanguageChange: ((language: Language) => void) | null, onCodeChange: ((code: string) => void) | null, diff --git a/frontend/src/components/game/PlayerGameView.tsx b/frontend/src/components/game/PlayerGameView.tsx index 79f589e0f..e3d1e403f 100644 --- a/frontend/src/components/game/PlayerGameView.tsx +++ b/frontend/src/components/game/PlayerGameView.tsx @@ -37,10 +37,12 @@ import { getDifficultyDisplayButton, InheritedTextButton, SmallButton } from '.. import { SpectatorBackIcon } from '../core/Icon'; import Language from '../../api/Language'; import { CopyIndicator, BottomCopyIndicatorContainer, InlineCopyIcon } from '../special/CopyIndicator'; -import { useAppSelector, useBestSubmission } from '../../util/Hook'; +import { useAppSelector, useBestSubmission, useGetSubmission } from '../../util/Hook'; import { routes, send, subscribe } from '../../api/Socket'; import { User } from '../../api/User'; -import { getScore, getSubmissionCount, getSubmissionTime } from '../../util/Utility'; +import { + getScore, getSubmissionCount, getSubmissionTime, getSubmission, +} from '../../util/Utility'; const StyledMarkdownEditor = styled(MarkdownEditor)` margin-top: 15px; @@ -164,8 +166,8 @@ function PlayerGameView(props: PlayerGameViewProps) { const [problems, setProblems] = useState([]); const [languageList, setLanguageList] = useState([Language.Java]); const [codeList, setCodeList] = useState(['']); - const [currentSubmission, setCurrentSubmission] = useState(null); const [currentProblemIndex, setCurrentProblemIndex] = useState(0); + let currentSubmission = useGetSubmission(currentProblemIndex, submissions); const [loading, setLoading] = useState(false); const [error, setError] = useState(gameError); @@ -189,18 +191,6 @@ function PlayerGameView(props: PlayerGameViewProps) { */ useBeforeunload(() => 'Leaving this page may cause you to lose your current code and data.'); - // const createCodeLanguageArray = () => { - // while (languageList.length < problems.length) { - // languageList.push(Language.Java); - // } - // - // while (codeList.length < problems.length) { - // codeList.push(''); - // } - // }; - - // useEffect(() => createCodeLanguageArray(), []); - const getCurrentLanguage = useCallback(() => languageList[currentProblemIndex], [languageList, currentProblemIndex]); @@ -222,17 +212,6 @@ function PlayerGameView(props: PlayerGameViewProps) { })); }; - // Returns the most recent submission made for problem of index curr. - const getSubmission = (curr: number, playerSubmissions: Submission[]) => { - for (let i = playerSubmissions.length - 1; i >= 0; i -= 1) { - if (playerSubmissions[i].problemIndex === curr) { - return playerSubmissions[i]; - } - } - - return null; - }; - // References necessary for the spectator subscription callback. const stateRef = useRef(); stateRef.current = { @@ -404,7 +383,7 @@ function PlayerGameView(props: PlayerGameViewProps) { // Set the 'test' submission type to correctly display result. // eslint-disable-next-line no-param-reassign res.submissionType = SubmissionType.Test; - setCurrentSubmission(res); + currentSubmission = res; }) .catch((err) => { setLoading(false); @@ -431,7 +410,6 @@ function PlayerGameView(props: PlayerGameViewProps) { // eslint-disable-next-line no-param-reassign res.submissionType = SubmissionType.Submit; setSubmissions(submissions.concat([res])); - setCurrentSubmission(res); }) .catch((err) => { setLoading(false); @@ -442,7 +420,6 @@ function PlayerGameView(props: PlayerGameViewProps) { // todo: no wrap const nextProblem = () => { setCurrentProblemIndex((currentProblemIndex + 1) % problems?.length); - setCurrentSubmission(getSubmission((currentProblemIndex + 1) % problems?.length, submissions)); }; const previousProblem = () => { @@ -453,7 +430,6 @@ function PlayerGameView(props: PlayerGameViewProps) { } setCurrentProblemIndex(temp); - setCurrentSubmission(getSubmission(temp, submissions)); }; const displayPlayerLeaderboard = useCallback(() => game?.players.map((player, index) => ( diff --git a/frontend/src/components/results/PlayerResultsItem.tsx b/frontend/src/components/results/PlayerResultsItem.tsx index 4d48a1633..466ade88d 100644 --- a/frontend/src/components/results/PlayerResultsItem.tsx +++ b/frontend/src/components/results/PlayerResultsItem.tsx @@ -6,7 +6,6 @@ import { Color } from '../../api/Color'; import { useBestSubmission, useGetScore, useGetSubmissionTime } from '../../util/Hook'; import Language, { displayNameFromLanguage } from '../../api/Language'; import { TextButton } from '../core/Button'; -// import { getScore, getSubmissionCount, getSubmissionTime } from '../../util/Utility'; const Content = styled.tr` border-radius: 5px; @@ -97,33 +96,13 @@ function PlayerResultsItem(props: PlayerResultsCardProps) { const time = useGetSubmissionTime(player); const bestSubmission : Submission | null = useBestSubmission(player); + const getSubmissionCount = () => player.submissions.length || '0'; + const getDisplayNickname = () => { const { nickname } = player.user; return `${nickname} ${isCurrentPlayer ? '(you)' : ''}`; }; - const getScore = () => { - if (!score) { - return '0'; - } - - const percent = Math.round((score / numProblems) * 100); - return `${percent}%`; - }; - - const getSubmissionTime = () => { - if (!time) { - return 'N/A'; - } - - const startTime = new Date(gameStartTime).getTime(); - const diffMilliseconds = new Date(time).getTime() - startTime; - const diffMinutes = Math.floor(diffMilliseconds / (60 * 1000)); - return `${diffMinutes}m ago`; - }; - - const getSubmissionCount = () => player.submissions.length || '0'; - const getSubmissionLanguage = () => { if (!bestSubmission) { return 'N/A'; @@ -153,10 +132,10 @@ function PlayerResultsItem(props: PlayerResultsCardProps) { - {getScore()} + {score} - {getSubmissionTime()} + {time} {getSubmissionCount()} diff --git a/frontend/src/util/Hook.tsx b/frontend/src/util/Hook.tsx index ac2cea8bc..09d30de9f 100644 --- a/frontend/src/util/Hook.tsx +++ b/frontend/src/util/Hook.tsx @@ -76,6 +76,22 @@ export const useGetSubmissionTime = (player?: Player) => { return time; }; +// Returns the most recent submission made for problem of index curr. +export const useGetSubmission = (curr: number, playerSubmissions: Submission[]) => { + const [submission, setSubmission] = useState(null); + + useEffect(() => { + for (let i = playerSubmissions.length - 1; i >= 0; i -= 1) { + if (playerSubmissions[i].problemIndex === curr) { + setSubmission(playerSubmissions[i]); + i = -1; + } + } + }, [curr, playerSubmissions]); + + return submission; +}; + export const useProblemEditable = (user: FirebaseUserType | null, problem: Problem | null) => { const [editable, setEditable] = useState(false); diff --git a/frontend/src/util/Utility.ts b/frontend/src/util/Utility.ts index adb7d742e..c6892f744 100644 --- a/frontend/src/util/Utility.ts +++ b/frontend/src/util/Utility.ts @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { User } from '../api/User'; import { removeUser } from '../api/Room'; import { disconnect } from '../api/Socket'; @@ -135,6 +135,14 @@ export const getSubmissionTime = (bestSubmission: Submission | null, export const getSubmissionCount = (player: Player | null) => player?.submissions.length || '0'; +export const getSubmission = (curr: number, playerSubmissions: Submission[]) => { + for (let i = playerSubmissions.length - 1; i >= 0; i -= 1) { + if (playerSubmissions[i].problemIndex === curr) { + return playerSubmissions[i]; + } + } +}; + /** * De-duplicate a list of SelectableProblems. * Solution at: https://stackoverflow.com/a/1584377/7517518. diff --git a/frontend/src/views/Lobby.tsx b/frontend/src/views/Lobby.tsx index 059703d65..43872ee22 100644 --- a/frontend/src/views/Lobby.tsx +++ b/frontend/src/views/Lobby.tsx @@ -497,12 +497,12 @@ function LobbyPage() { }; updateRoomSettings(currentRoomId, settings) - .then(() => setLoading(false)) .catch((err) => { - setLoading(false); setError(err.message); // Set numProblems back to original if REST call failed setNumProblems(prevNumProblems); + }).finally(() => { + setLoading(false); }); }; diff --git a/src/main/java/com/codejoust/main/service/SubmitService.java b/src/main/java/com/codejoust/main/service/SubmitService.java index a2f367f65..f3e113faa 100644 --- a/src/main/java/com/codejoust/main/service/SubmitService.java +++ b/src/main/java/com/codejoust/main/service/SubmitService.java @@ -92,7 +92,6 @@ private Submission getDummySubmission(TesterRequest request) { // Test the submission and send a socket update. public SubmissionDto runCode(Game game, SubmissionRequest request) { String userId = request.getInitiator().getUserId(); - Player player = game.getPlayers().get(userId); PlayerCode playerCode = new PlayerCode(); playerCode.setCode(request.getCode()); From 1be423b061ca09ed1576969e8a1fb9efb65ac729 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Wed, 30 Jun 2021 19:57:01 -0700 Subject: [PATCH 35/37] final nits, hopefully --- frontend/src/components/game/Editor.tsx | 40 ++++++++----------- .../src/components/game/PlayerGameView.tsx | 10 ++--- .../components/results/PreviewCodeContent.tsx | 3 +- 3 files changed, 21 insertions(+), 32 deletions(-) diff --git a/frontend/src/components/game/Editor.tsx b/frontend/src/components/game/Editor.tsx index 7efb75ba0..5f670f292 100644 --- a/frontend/src/components/game/Editor.tsx +++ b/frontend/src/components/game/Editor.tsx @@ -8,19 +8,16 @@ import { DefaultCodeType } from '../../api/Problem'; /** * onLanguageChange - a callback, called when the currentLanguage changes * onCodeChange - a callback, called when the code changes - * getCurrentLanguage - a function passed in by the PlayerGameView which can be called to get the - * working language + * getCurrentLanguage - a function passed in, which can be called to get the current language * defaultCodeMap - a map of all the default code for each problem and language - * defaultLanguage - the default language for the editor * currentProblem - the problem which is current being worked on - * liveCode - ???? + * liveCode - a string used for spectator view */ type EditorProps = { onLanguageChange: ((language: Language) => void) | null, onCodeChange: ((code: string) => void) | null, getCurrentLanguage: (() => Language) | null, defaultCodeMap: DefaultCodeType[] | null, - defaultLanguage: Language, defaultCode: string | null, currentProblem: number, liveCode: string | null, @@ -102,18 +99,14 @@ const monacoEditorOptions: EditorConstructionOptions = { function ResizableMonacoEditor(props: EditorProps) { const { onLanguageChange, onCodeChange, getCurrentLanguage, - defaultCodeMap, defaultLanguage, defaultCode, currentProblem, liveCode, + defaultCodeMap, defaultCode, currentProblem, liveCode, } = props; const theme = useContext(ThemeContext); - const [currentLanguage, setCurrentLanguage] = useState(defaultLanguage); const [codeEditor, setCodeEditor] = useState(null); const [codeMap, setCodeMap] = useState(defaultCodeMap); const [previousProblem, setPreviousProblem] = useState(0); - - useEffect(() => { - setCurrentLanguage(defaultLanguage); - }, [defaultLanguage]); + const [currentLanguage, setCurrentLanguage] = useState(Language.Java); useEffect(() => { setCodeMap(defaultCodeMap); @@ -150,18 +143,19 @@ function ResizableMonacoEditor(props: EditorProps) { codeEditor.setValue(codeMap[currentProblem][language]); } - // Change the language and initial code for the editor - setCurrentLanguage(language); + // Call onLanguageChange if (onLanguageChange) { onLanguageChange(language); } }; + // This hook will be called if the user switches the problem he or she is working on useEffect(() => { if (codeMap != null && codeEditor != null) { if (codeMap[currentProblem] != null) { const codeMapTemp = codeMap; + // If the value of the editor is not "Loading...", save it in the codeMap if (codeEditor.getValue() !== 'Loading...') { codeMapTemp[previousProblem][currentLanguage] = codeEditor.getValue(); } @@ -169,21 +163,19 @@ function ResizableMonacoEditor(props: EditorProps) { setCodeMap(codeMapTemp); setPreviousProblem(currentProblem); - let newLanguage = currentLanguage; - - if (getCurrentLanguage !== null) { - newLanguage = getCurrentLanguage(); - - if (newLanguage !== currentLanguage) { - setCurrentLanguage(newLanguage); - } + // If getCurrentLanguage is defined, set currentLanguage to the returned value, + // which is the language that was last used for the new problem, + // and set the CodeEditor to be the code for the problem and language + if (getCurrentLanguage) { + setCurrentLanguage(getCurrentLanguage()); + codeEditor.setValue(codeMap[currentProblem][getCurrentLanguage()]); + } else { + codeEditor.setValue(codeMap[currentProblem][currentLanguage]); } - - codeEditor.setValue(codeMap[currentProblem][newLanguage]); } } }, [currentLanguage, codeMap, codeEditor, setCodeEditor, - currentProblem, previousProblem, getCurrentLanguage]); + currentProblem, previousProblem]); return ( diff --git a/frontend/src/components/game/PlayerGameView.tsx b/frontend/src/components/game/PlayerGameView.tsx index 5217dde45..3a93948b3 100644 --- a/frontend/src/components/game/PlayerGameView.tsx +++ b/frontend/src/components/game/PlayerGameView.tsx @@ -137,7 +137,7 @@ type StateRefType = { currentUser: User | null, currentCode: string, currentLanguage: string, - currentIndex: number, + currentProblemIndex: number, } /** @@ -219,7 +219,7 @@ function PlayerGameView(props: PlayerGameViewProps) { currentUser, currentCode: codeList[currentProblemIndex], currentLanguage: languageList[currentProblemIndex], - currentIndex: currentProblemIndex, + currentProblemIndex, }; const setDefaultCodeFromProblems = useCallback((problemsParam: Problem[], @@ -299,7 +299,7 @@ function PlayerGameView(props: PlayerGameViewProps) { if (JSON.parse(result.body).newSpectator) { sendViewUpdate(stateRef.current?.game, stateRef.current?.currentUser, stateRef.current?.currentCode, stateRef.current?.currentLanguage, - stateRef.current?.currentIndex); + stateRef.current?.currentProblemIndex); } }; @@ -553,7 +553,6 @@ function PlayerGameView(props: PlayerGameViewProps) { getCurrentLanguage={getCurrentLanguage} defaultCodeMap={defaultCodeList} currentProblem={currentProblemIndex} - defaultLanguage={Language.Java} defaultCode={null} liveCode={null} /> @@ -572,8 +571,7 @@ function PlayerGameView(props: PlayerGameViewProps) { spectateGame?.language as Language} + getCurrentLanguage={() => spectateGame?.language as Language || Language.Java} defaultCodeMap={null} currentProblem={currentProblemIndex} defaultCode={spectateGame?.code} diff --git a/frontend/src/components/results/PreviewCodeContent.tsx b/frontend/src/components/results/PreviewCodeContent.tsx index f823d1903..fb6148739 100644 --- a/frontend/src/components/results/PreviewCodeContent.tsx +++ b/frontend/src/components/results/PreviewCodeContent.tsx @@ -45,10 +45,9 @@ function PreviewCodeContent(props: PreviewCodeContentProps) { bestSubmission?.language as Language || Language.Java} defaultCodeMap={null} currentProblem={0} - defaultLanguage={bestSubmission?.language as Language || Language.Java} defaultCode={bestSubmission?.code || 'Uh oh! An error occurred fetching this player\'s code'} liveCode={null} /> From 907ed8c84a921334fc04f8408517ae42d8d493cd Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Fri, 2 Jul 2021 20:32:23 -0700 Subject: [PATCH 36/37] fixing time and making solved array --- frontend/src/api/Game.ts | 2 +- frontend/src/components/card/LeaderboardCard.tsx | 2 +- .../src/components/results/PlayerResultsItem.tsx | 13 ++++++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/frontend/src/api/Game.ts b/frontend/src/api/Game.ts index 696bea046..96aa5ea40 100644 --- a/frontend/src/api/Game.ts +++ b/frontend/src/api/Game.ts @@ -9,7 +9,7 @@ import { Color } from './Color'; export type Player = { user: User, submissions: Submission[], - solved: boolean, + solved: boolean[], color: Color, }; diff --git a/frontend/src/components/card/LeaderboardCard.tsx b/frontend/src/components/card/LeaderboardCard.tsx index b045486a1..bbd936666 100644 --- a/frontend/src/components/card/LeaderboardCard.tsx +++ b/frontend/src/components/card/LeaderboardCard.tsx @@ -115,7 +115,7 @@ function LeaderboardCard(props: LeaderboardCardProps) { nickname={player.user.nickname} active={Boolean(player.user.sessionId)} /> - {`${place}.${getScorePercentage()}`} + !element).length === 0}>{`${place}.${getScorePercentage()}`} {showHover ? ( diff --git a/frontend/src/components/results/PlayerResultsItem.tsx b/frontend/src/components/results/PlayerResultsItem.tsx index 466ade88d..f7df44621 100644 --- a/frontend/src/components/results/PlayerResultsItem.tsx +++ b/frontend/src/components/results/PlayerResultsItem.tsx @@ -103,6 +103,17 @@ function PlayerResultsItem(props: PlayerResultsCardProps) { return `${nickname} ${isCurrentPlayer ? '(you)' : ''}`; }; + const getSubmissionTime = () => { + if (!time) { + return 'Never'; + } + + const currentTime = new Date().getTime(); + const diffMilliseconds = currentTime - new Date(time).getTime(); + const diffMinutes = Math.floor(diffMilliseconds / (60 * 1000)); + return `${diffMinutes}m ago`; + }; + const getSubmissionLanguage = () => { if (!bestSubmission) { return 'N/A'; @@ -135,7 +146,7 @@ function PlayerResultsItem(props: PlayerResultsCardProps) { {score} - {time} + {getSubmissionTime()} {getSubmissionCount()} From f546c74c60b338a01136d99f764a0dbcf45e4994 Mon Sep 17 00:00:00 2001 From: jasonz6688 Date: Mon, 5 Jul 2021 17:37:55 -0700 Subject: [PATCH 37/37] making code SonarLint compliant --- frontend/src/components/game/Editor.tsx | 2 +- frontend/src/components/results/PlayerResultsItem.tsx | 2 +- frontend/src/util/Utility.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/game/Editor.tsx b/frontend/src/components/game/Editor.tsx index 5f670f292..9409d625a 100644 --- a/frontend/src/components/game/Editor.tsx +++ b/frontend/src/components/game/Editor.tsx @@ -174,7 +174,7 @@ function ResizableMonacoEditor(props: EditorProps) { } } } - }, [currentLanguage, codeMap, codeEditor, setCodeEditor, + }, [currentLanguage, codeMap, codeEditor, setCodeEditor, getCurrentLanguage, currentProblem, previousProblem]); return ( diff --git a/frontend/src/components/results/PlayerResultsItem.tsx b/frontend/src/components/results/PlayerResultsItem.tsx index f7df44621..462696f76 100644 --- a/frontend/src/components/results/PlayerResultsItem.tsx +++ b/frontend/src/components/results/PlayerResultsItem.tsx @@ -89,7 +89,7 @@ type PlayerResultsCardProps = { function PlayerResultsItem(props: PlayerResultsCardProps) { const { - player, place, isCurrentPlayer, color, gameStartTime, numProblems, onViewCode, onSpectateLive, + player, place, isCurrentPlayer, color, onViewCode, onSpectateLive, } = props; const score = useGetScore(player); diff --git a/frontend/src/util/Utility.ts b/frontend/src/util/Utility.ts index 57f60fc77..96b1277e5 100644 --- a/frontend/src/util/Utility.ts +++ b/frontend/src/util/Utility.ts @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import { User } from '../api/User'; import { removeUser } from '../api/Room'; import { disconnect } from '../api/Socket';