From 088ba5bbd9eb390ae87d5c49acf22727302a51e1 Mon Sep 17 00:00:00 2001 From: nithin balan Date: Sat, 3 Feb 2024 13:20:30 +0530 Subject: [PATCH 1/8] feat: add pvp editor state and leaderboard --- docs/spec/CodeCharacter-API.yml | 405 +++++++++++++- packages/client/.openapi-generator/FILES | 2 + packages/client/.openapi-generator/VERSION | 2 +- packages/client/src/apis/AuthApi.ts | 4 +- packages/client/src/apis/CodeApi.ts | 4 +- packages/client/src/apis/CurrentUserApi.ts | 4 +- .../client/src/apis/DailyChallengesApi.ts | 91 +++- packages/client/src/apis/GameApi.ts | 8 +- packages/client/src/apis/LeaderboardApi.ts | 4 +- packages/client/src/apis/MapApi.ts | 4 +- packages/client/src/apis/MatchApi.ts | 147 +++++- packages/client/src/apis/NotificationApi.ts | 4 +- packages/client/src/apis/PvpGameApi.ts | 119 +++++ packages/client/src/apis/PvpLeaderboardApi.ts | 118 +++++ packages/client/src/apis/UserApi.ts | 4 +- packages/client/src/apis/index.ts | 2 + packages/client/src/index.ts | 4 +- packages/client/src/models/index.ts | 152 ++++++ packages/client/src/runtime.ts | 43 +- packages/client/tsconfig.esm.json | 7 + src/assets/codes | 2 +- src/components/Editor/Editor.tsx | 31 +- src/components/Editor/EditorTypes.ts | 1 + src/components/EditorInfo/EditorInfo.tsx | 4 +- .../Leaderboard/DailyLeaderboard.module.css | 196 ------- .../Leaderboard/DailyLeaderboard.tsx | 167 ------ .../Leaderboard/Leaderboard.module.css | 72 +++ src/components/Leaderboard/Leaderboard.tsx | 493 +++++++++++++----- .../Leaderboard/LeaderboardTypes.ts | 9 + .../Leaderboard/MainLeaderboard.tsx | 117 ++++- src/components/NavBar/NavBar.tsx | 4 + .../PvPSelfMatchMakeModal.tsx | 201 +++++++ .../SelfMatchMakeModal.tsx | 4 +- src/main.tsx | 4 + src/pages/Dashboard/Dashboard.module.css | 19 + src/pages/Dashboard/Dashboard.tsx | 172 +++++- src/store/DailyChallenge/dailyChallenge.ts | 6 +- src/store/PvP/pvpCode.ts | 77 +++ .../PvPSelfMatchModal.ts | 61 +++ src/store/store.ts | 4 + 40 files changed, 2189 insertions(+), 583 deletions(-) create mode 100644 packages/client/src/apis/PvpGameApi.ts create mode 100644 packages/client/src/apis/PvpLeaderboardApi.ts create mode 100644 packages/client/tsconfig.esm.json delete mode 100644 src/components/Leaderboard/DailyLeaderboard.module.css delete mode 100644 src/components/Leaderboard/DailyLeaderboard.tsx create mode 100644 src/components/Leaderboard/LeaderboardTypes.ts create mode 100644 src/components/PvPSelfMatchMakingModal/PvPSelfMatchMakeModal.tsx create mode 100644 src/store/PvP/pvpCode.ts create mode 100644 src/store/PvPSelfMatchMakeModal/PvPSelfMatchModal.ts diff --git a/docs/spec/CodeCharacter-API.yml b/docs/spec/CodeCharacter-API.yml index cc7960a..8c91af0 100644 --- a/docs/spec/CodeCharacter-API.yml +++ b/docs/spec/CodeCharacter-API.yml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: CodeCharacter API - version: 2023.0.1 + version: 2024.0.1 contact: name: CodeCharacter Authors url: 'https://delta.nitt.edu' @@ -305,6 +305,160 @@ paths: description: Get leaderboard parameters: [] + /pvpleaderboard: + get: + summary: Get PvP leaderboard + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PvPLeaderBoardResponse' + examples: + Example: + value: + - user: + username: testUser + name: >- + Test User1 + asdfdsfasdfasdfadsfasdfadsdfasdfasdfasfasdfsfsdfsfadsfasfa + country: IN + college: NIT Trichy + avatarId: 0 + stats: + rating: 500 + wins: 6 + losses: 0 + ties: 1 + - user: + username: testUser2 + name: >- + Test User2 + asdfdsfasdfasdfadsfasdfadsdfasdfasdfasfasdfsfsdfsfadsfasfa + country: IN + college: NIT Trichy + avatarId: 1 + stats: + rating: 50 + wins: 3 + losses: 3 + ties: 0 + - user: + username: testUser3 + name: >- + Test User3 + asdfdsfasdfasdfadsfasdfadsdfasdfasdfasfasdfsfsdfsfadsfasfa + country: IN + college: NIT Trichy + avatarId: 0 + stats: + rating: 80 + wins: 1 + losses: 0 + ties: 0 + - user: + username: testUser4 + name: Test User4 + country: IN + college: NIT Trichy + avatarId: 0 + stats: + rating: 230 + wins: 0 + losses: 0 + ties: 3 + - user: + username: testUser5 + name: Test User5 Test Test + country: IN + college: NIT Trichy + avatarId: 0 + stats: + rating: 850 + wins: 0 + losses: 0 + ties: 4 + - user: + username: testUser6 + name: >- + Test User6 + asdfdsfasdfasdfadsfasdfadsdfasdfasdfasfasdfsfsdfsfadsfasfa + country: IN + college: NIT Trichy + avatarId: 0 + stats: + rating: 600 + wins: 0 + losses: 0 + ties: 2 + - user: + username: testUser7 + name: >- + Test User7 + asdfdsfasdfasdfadsfasdfadsdfasdfasdfasfasdfsfsdfsfadsfasfa + country: IN + college: NIT Trichy + avatarId: 0 + stats: + rating: 57 + wins: 20 + losses: 70 + ties: 1 + - user: + username: testUser8 + name: Test User8 + country: IN + college: NIT Trichy + avatarId: 0 + stats: + rating: 850 + wins: 0 + losses: 0 + ties: 0 + - user: + username: testUser9 + name: Test User9 + country: IN + college: NIT Trichy + avatarId: 0 + stats: + rating: 78 + wins: 0 + losses: 0 + ties: 4 + - user: + username: testUser10 + name: Test User10 + country: IN + college: NIT Trichy + avatarId: 0 + stats: + rating: 10 + wins: 0 + losses: 0 + ties: 2 + '401': + description: Unauthorized + operationId: getPvPLeaderboard + tags: + - pvpLeaderboard + parameters: + - schema: + type: integer + in: query + name: page + description: Index of the page + - schema: + type: integer + in: query + name: size + description: Size of the page + description: Get PvP leaderboard + parameters: [] + /top-matches: get: summary: Get top matches @@ -318,7 +472,7 @@ paths: schema: type: array items: - $ref: '#/components/schemas/Match' + type: object examples: Example: value: @@ -514,6 +668,25 @@ paths: value: aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1kUXc0dzlXZ1hjUQ== operationId: getGameLogsByGameId description: Get game logs by game ID + '/pvpgames/{gameId}/logs': + parameters: + - $ref: '#/components/parameters/gameId' + get: + summary: Get pvp game logs by game ID + tags: + - pvp-game + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PvPGameLog' + examples: + Example: + value: aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1kUXc0dzlXZ1hjUQ== + operationId: getPvpGameLogsByGameId + description: Get pvp game logs by game ID /user: parameters: [] get: @@ -997,8 +1170,19 @@ paths: avatarId: 0 '401': description: Unauthorized - operationId: getUserMatches - description: Get matches played by authenticated user + operationId: getUserNormalMatches + description: Get normal matches played by authenticated user + parameters: + - schema: + type: integer + in: query + name: page + description: Index of the page + - schema: + type: integer + in: query + name: size + description: Size of the page parameters: [] post: summary: Create match @@ -1035,6 +1219,60 @@ paths: opponentId: 0a4b34b0-6057-4b82-ae27-a59c36eab667 mapRevisionId: f52ddf9e-e933-471a-9250-41078cc39f80 codeRevisionId: d9eb9923-651b-4ec4-b6f1-b7625b2a9392 + /user/pvpmatches: + get: + summary: Get user pvp matches + tags: + - match + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PvPMatch' + examples: + Example: + value: + - id: 497f6eca-6276-4993-bfeb-53cbbbba6f08 + - game: + - id: 497f6eca-6276-4993-bfeb-53cbbbba6f08 + scorePlayer1: 100 + scorePlayer2: 45 + status: IDLE + - matchMode: PVPSELF + - matchVerdict: PLAYER1 + - createdAt: '2019-08-24T14:15:22Z' + - user1: + username: testUser + name: Test User + country: IN + college: NIT Trichy + avatarId: 0 + - user2: + username: testUser + name: Test User + country: IN + college: NIT Trichy + avatarId: 0 + '401': + description: Unauthorized + operationId: getUserPvPMatches + description: Get pvp matches played by authenticated user + parameters: + - schema: + type: integer + in: query + name: page + description: Index of the page + - schema: + type: integer + in: query + name: size + description: Size of the page + parameters: [] /user/notifications: get: summary: Get all notifications @@ -1292,6 +1530,55 @@ paths: value: '[[0,0,0]]' tags: - Daily Challenges + /dc/matches: + get: + summary: Get user daily challenge matches + tags: + - Daily Challenges + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Match' + examples: + Example: + value: + - id: 497f6eca-6276-4993-bfeb-53cbbbba6f08 + games: + - id: 497f6eca-6276-4993-bfeb-53cbbbba6f08 + coinsUsed: 100 + destruction: 45 + status: IDLE + matchMode: SELF + matchVerdict: SUCCESS + createdAt: '2019-08-24T14:15:22Z' + user1: + username: testUser + name: Test User + country: IN + college: NIT Trichy + avatarId: 0 + user2: null + '401': + description: Unauthorized + operationId: getUserDCMatches + description: Get daily-challenge matches played by authenticated user + parameters: + - schema: + type: integer + in: query + name: page + description: Index of the page + - schema: + type: integer + in: query + name: size + description: Size of the page + parameters: [] components: schemas: @@ -1435,6 +1722,29 @@ components: - wins - losses - ties + PvPUserStats: + title: PvPUserStats + type: object + description: PvP User stats model + properties: + rating: + type: number + example: 1000 + wins: + type: integer + default: 0 + example: 1 + losses: + type: integer + example: 1 + ties: + type: integer + example: 1 + required: + - rating + - wins + - losses + - ties Code: title: Code type: object @@ -1871,6 +2181,36 @@ components: - matchVerdict - createdAt - user1 + PvPMatch: + description: PvP Match model + type: object + properties: + id: + type: string + format: uuid + example: 123e4567-e89b-12d3-a456-426614174000 + game: + $ref: '#/components/schemas/PvPGame' + matchMode: + $ref: '#/components/schemas/MatchMode' + matchVerdict: + $ref: '#/components/schemas/Verdict' + createdAt: + type: string + format: date-time + example: '2021-01-01T00:00:00Z' + user1: + $ref: '#/components/schemas/PublicUser' + user2: + $ref: '#/components/schemas/PublicUser' + required: + - id + - game + - matchMode + - matchVerdict + - createdAt + - user1 + - user2 CreateMatchRequest: title: CreateMatchRequest type: object @@ -1899,6 +2239,11 @@ components: format: uuid description: Revision of the code nullable: true + codeRevisionId2: + type: string + format: uuid + description: Revision of the code (for SELF-PVP mode) + nullable: true required: - mode Game: @@ -1926,6 +2271,33 @@ components: type: string description: Game log model example: aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1kUXc0dzlXZ1hjUQ== + + PvPGame: + title: PvPGame + type: object + description: PvP Game model + properties: + id: + type: string + format: uuid + example: 123e4567-e89b-12d3-a456-426614174000 + scorePlayer1: + type: integer + example: 69 + scorePlayer2: + type: integer + example: 69 + status: + $ref: '#/components/schemas/PvPGameStatus' + required: + - id + - scorePlayer1 + - scorePlayer2 + - status + PvPGameLog: + type: string + description: PvP Game log model + example: aHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1kUXc0dzlXZ1hjUQ== LeaderboardEntry: title: LeaderboardEntry type: object @@ -1935,9 +2307,22 @@ components: stats: $ref: '#/components/schemas/UserStats' required: + - user - user - stats description: Leaderboard entry model + PvPLeaderBoardResponse: + title: PvPLeaderboardResponse + description: Response model for PvP leaderboard + type: object + properties: + user: + $ref: '#/components/schemas/PublicUser' + stats: + $ref: '#/components/schemas/PvPUserStats' + required: + - user + - stats DailyChallengeGetRequest: title: Get daily challenge @@ -2031,6 +2416,8 @@ components: - MANUAL - AUTO - DAILYCHALLENGE + - PVP + - SELFPVP description: Match Mode Verdict: type: string @@ -2050,6 +2437,14 @@ components: - EXECUTING - EXECUTED - EXECUTE_ERROR + PvPGameStatus: + title: PvPGameStatus + type: string + enum: + - IDLE + - EXECUTING + - EXECUTED + - EXECUTE_ERROR AuthStatusResponse: title: AuthStatusResponse type: object @@ -2080,6 +2475,7 @@ components: enum: - NORMAL - DAILY_CHALLENGE + - PVP default: NORMAL GameMapType: title: GameMapType @@ -2160,6 +2556,7 @@ tags: - name: code - name: current-user - name: game + - name: pvp-game - name: leaderboard - name: map - name: match diff --git a/packages/client/.openapi-generator/FILES b/packages/client/.openapi-generator/FILES index 54eb0a0..53a949a 100644 --- a/packages/client/.openapi-generator/FILES +++ b/packages/client/.openapi-generator/FILES @@ -8,6 +8,8 @@ src/apis/LeaderboardApi.ts src/apis/MapApi.ts src/apis/MatchApi.ts src/apis/NotificationApi.ts +src/apis/PvpGameApi.ts +src/apis/PvpLeaderboardApi.ts src/apis/UserApi.ts src/apis/index.ts src/index.ts diff --git a/packages/client/.openapi-generator/VERSION b/packages/client/.openapi-generator/VERSION index e7e42a4..4b49d9b 100644 --- a/packages/client/.openapi-generator/VERSION +++ b/packages/client/.openapi-generator/VERSION @@ -1 +1 @@ -6.3.0 \ No newline at end of file +7.2.0 \ No newline at end of file diff --git a/packages/client/src/apis/AuthApi.ts b/packages/client/src/apis/AuthApi.ts index 35c63ff..39e434f 100644 --- a/packages/client/src/apis/AuthApi.ts +++ b/packages/client/src/apis/AuthApi.ts @@ -4,7 +4,7 @@ * CodeCharacter API * Specification of the CodeCharacter API * - * The version of the OpenAPI document: 2023.0.1 + * The version of the OpenAPI document: 2024.0.1 * Contact: delta@nitt.edu * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -20,7 +20,7 @@ import type { PasswordLoginRequest, PasswordLoginResponse, ResetPasswordRequest, -} from '../models'; +} from '../models/index'; export interface ForgotPasswordOperationRequest { forgotPasswordRequest: ForgotPasswordRequest; diff --git a/packages/client/src/apis/CodeApi.ts b/packages/client/src/apis/CodeApi.ts index ccac66c..b9db2b5 100644 --- a/packages/client/src/apis/CodeApi.ts +++ b/packages/client/src/apis/CodeApi.ts @@ -4,7 +4,7 @@ * CodeCharacter API * Specification of the CodeCharacter API * - * The version of the OpenAPI document: 2023.0.1 + * The version of the OpenAPI document: 2024.0.1 * Contact: delta@nitt.edu * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -20,7 +20,7 @@ import type { CreateCodeRevisionRequest, GenericError, UpdateLatestCodeRequest, -} from '../models'; +} from '../models/index'; export interface CreateCodeRevisionOperationRequest { createCodeRevisionRequest: CreateCodeRevisionRequest; diff --git a/packages/client/src/apis/CurrentUserApi.ts b/packages/client/src/apis/CurrentUserApi.ts index 5e2ec49..7589b71 100644 --- a/packages/client/src/apis/CurrentUserApi.ts +++ b/packages/client/src/apis/CurrentUserApi.ts @@ -4,7 +4,7 @@ * CodeCharacter API * Specification of the CodeCharacter API * - * The version of the OpenAPI document: 2023.0.1 + * The version of the OpenAPI document: 2024.0.1 * Contact: delta@nitt.edu * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -19,7 +19,7 @@ import type { GenericError, UpdateCurrentUserProfile, UpdatePasswordRequest, -} from '../models'; +} from '../models/index'; export interface CompleteUserProfileRequest { completeProfileRequest: CompleteProfileRequest; diff --git a/packages/client/src/apis/DailyChallengesApi.ts b/packages/client/src/apis/DailyChallengesApi.ts index ccd2a6e..d039910 100644 --- a/packages/client/src/apis/DailyChallengesApi.ts +++ b/packages/client/src/apis/DailyChallengesApi.ts @@ -4,7 +4,7 @@ * CodeCharacter API * Specification of the CodeCharacter API * - * The version of the OpenAPI document: 2023.0.1 + * The version of the OpenAPI document: 2024.0.1 * Contact: delta@nitt.edu * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -18,7 +18,8 @@ import type { DailyChallengeLeaderBoardResponse, DailyChallengeMatchRequest, GenericError, -} from '../models'; + Match, +} from '../models/index'; export interface CreateDailyChallengeMatchRequest { dailyChallengeMatchRequest: DailyChallengeMatchRequest; @@ -29,6 +30,11 @@ export interface GetDailyChallengeLeaderBoardRequest { size?: number; } +export interface GetUserDCMatchesRequest { + page?: number; + size?: number; +} + /** * DailyChallengesApi - interface * @@ -100,6 +106,30 @@ export interface DailyChallengesApiInterface { size?: number, initOverrides?: RequestInit | runtime.InitOverrideFunction, ): Promise>; + + /** + * Get daily-challenge matches played by authenticated user + * @summary Get user daily challenge matches + * @param {number} [page] Index of the page + * @param {number} [size] Size of the page + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DailyChallengesApiInterface + */ + getUserDCMatchesRaw( + requestParameters: GetUserDCMatchesRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise>>; + + /** + * Get daily-challenge matches played by authenticated user + * Get user daily challenge matches + */ + getUserDCMatches( + page?: number, + size?: number, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise>; } /** @@ -268,4 +298,61 @@ export class DailyChallengesApi ); return await response.value(); } + + /** + * Get daily-challenge matches played by authenticated user + * Get user daily challenge matches + */ + async getUserDCMatchesRaw( + requestParameters: GetUserDCMatchesRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise>> { + const queryParameters: any = {}; + + if (requestParameters.page !== undefined) { + queryParameters['page'] = requestParameters.page; + } + + if (requestParameters.size !== undefined) { + queryParameters['size'] = requestParameters.size; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token('http-bearer', []); + + if (tokenString) { + headerParameters['Authorization'] = `Bearer ${tokenString}`; + } + } + const response = await this.request( + { + path: `/dc/matches`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, + initOverrides, + ); + + return new runtime.JSONApiResponse(response); + } + + /** + * Get daily-challenge matches played by authenticated user + * Get user daily challenge matches + */ + async getUserDCMatches( + page?: number, + size?: number, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + const response = await this.getUserDCMatchesRaw( + { page: page, size: size }, + initOverrides, + ); + return await response.value(); + } } diff --git a/packages/client/src/apis/GameApi.ts b/packages/client/src/apis/GameApi.ts index 4147bef..75e567d 100644 --- a/packages/client/src/apis/GameApi.ts +++ b/packages/client/src/apis/GameApi.ts @@ -4,7 +4,7 @@ * CodeCharacter API * Specification of the CodeCharacter API * - * The version of the OpenAPI document: 2023.0.1 + * The version of the OpenAPI document: 2024.0.1 * Contact: delta@nitt.edu * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -95,7 +95,11 @@ export class GameApi extends runtime.BaseAPI implements GameApiInterface { initOverrides, ); - return new runtime.TextApiResponse(response) as any; + if (this.isJsonMime(response.headers.get('content-type'))) { + return new runtime.JSONApiResponse(response); + } else { + return new runtime.TextApiResponse(response) as any; + } } /** diff --git a/packages/client/src/apis/LeaderboardApi.ts b/packages/client/src/apis/LeaderboardApi.ts index c74af00..d8a11d6 100644 --- a/packages/client/src/apis/LeaderboardApi.ts +++ b/packages/client/src/apis/LeaderboardApi.ts @@ -4,7 +4,7 @@ * CodeCharacter API * Specification of the CodeCharacter API * - * The version of the OpenAPI document: 2023.0.1 + * The version of the OpenAPI document: 2024.0.1 * Contact: delta@nitt.edu * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -13,7 +13,7 @@ */ import * as runtime from '../runtime'; -import type { LeaderboardEntry, TierType } from '../models'; +import type { LeaderboardEntry, TierType } from '../models/index'; export interface GetLeaderboardRequest { page?: number; diff --git a/packages/client/src/apis/MapApi.ts b/packages/client/src/apis/MapApi.ts index b02e58d..d038e6e 100644 --- a/packages/client/src/apis/MapApi.ts +++ b/packages/client/src/apis/MapApi.ts @@ -4,7 +4,7 @@ * CodeCharacter API * Specification of the CodeCharacter API * - * The version of the OpenAPI document: 2023.0.1 + * The version of the OpenAPI document: 2024.0.1 * Contact: delta@nitt.edu * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -21,7 +21,7 @@ import type { GenericError, MapCommitByCommitIdResponse, UpdateLatestMapRequest, -} from '../models'; +} from '../models/index'; export interface CreateMapRevisionOperationRequest { createMapRevisionRequest: CreateMapRevisionRequest; diff --git a/packages/client/src/apis/MatchApi.ts b/packages/client/src/apis/MatchApi.ts index 53566d6..483d6e0 100644 --- a/packages/client/src/apis/MatchApi.ts +++ b/packages/client/src/apis/MatchApi.ts @@ -4,7 +4,7 @@ * CodeCharacter API * Specification of the CodeCharacter API * - * The version of the OpenAPI document: 2023.0.1 + * The version of the OpenAPI document: 2024.0.1 * Contact: delta@nitt.edu * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -13,12 +13,27 @@ */ import * as runtime from '../runtime'; -import type { CreateMatchRequest, GenericError, Match } from '../models'; +import type { + CreateMatchRequest, + GenericError, + Match, + PvPMatch, +} from '../models/index'; export interface CreateMatchOperationRequest { createMatchRequest: CreateMatchRequest; } +export interface GetUserNormalMatchesRequest { + page?: number; + size?: number; +} + +export interface GetUserPvPMatchesRequest { + page?: number; + size?: number; +} + /** * MatchApi - interface * @@ -57,7 +72,7 @@ export interface MatchApiInterface { */ getTopMatchesRaw( initOverrides?: RequestInit | runtime.InitOverrideFunction, - ): Promise>>; + ): Promise>>; /** * Get top matches @@ -65,26 +80,55 @@ export interface MatchApiInterface { */ getTopMatches( initOverrides?: RequestInit | runtime.InitOverrideFunction, - ): Promise>; + ): Promise>; /** - * Get matches played by authenticated user + * Get normal matches played by authenticated user * @summary Get user matches + * @param {number} [page] Index of the page + * @param {number} [size] Size of the page * @param {*} [options] Override http request option. * @throws {RequiredError} * @memberof MatchApiInterface */ - getUserMatchesRaw( + getUserNormalMatchesRaw( + requestParameters: GetUserNormalMatchesRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction, ): Promise>>; /** - * Get matches played by authenticated user + * Get normal matches played by authenticated user * Get user matches */ - getUserMatches( + getUserNormalMatches( + page?: number, + size?: number, initOverrides?: RequestInit | runtime.InitOverrideFunction, ): Promise>; + + /** + * Get pvp matches played by authenticated user + * @summary Get user pvp matches + * @param {number} [page] Index of the page + * @param {number} [size] Size of the page + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof MatchApiInterface + */ + getUserPvPMatchesRaw( + requestParameters: GetUserPvPMatchesRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise>>; + + /** + * Get pvp matches played by authenticated user + * Get user pvp matches + */ + getUserPvPMatches( + page?: number, + size?: number, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise>; } /** @@ -157,7 +201,7 @@ export class MatchApi extends runtime.BaseAPI implements MatchApiInterface { */ async getTopMatchesRaw( initOverrides?: RequestInit | runtime.InitOverrideFunction, - ): Promise>> { + ): Promise>> { const queryParameters: any = {}; const headerParameters: runtime.HTTPHeaders = {}; @@ -180,7 +224,7 @@ export class MatchApi extends runtime.BaseAPI implements MatchApiInterface { initOverrides, ); - return new runtime.JSONApiResponse(response); + return new runtime.JSONApiResponse(response); } /** @@ -189,20 +233,29 @@ export class MatchApi extends runtime.BaseAPI implements MatchApiInterface { */ async getTopMatches( initOverrides?: RequestInit | runtime.InitOverrideFunction, - ): Promise> { + ): Promise> { const response = await this.getTopMatchesRaw(initOverrides); return await response.value(); } /** - * Get matches played by authenticated user + * Get normal matches played by authenticated user * Get user matches */ - async getUserMatchesRaw( + async getUserNormalMatchesRaw( + requestParameters: GetUserNormalMatchesRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction, ): Promise>> { const queryParameters: any = {}; + if (requestParameters.page !== undefined) { + queryParameters['page'] = requestParameters.page; + } + + if (requestParameters.size !== undefined) { + queryParameters['size'] = requestParameters.size; + } + const headerParameters: runtime.HTTPHeaders = {}; if (this.configuration && this.configuration.accessToken) { @@ -227,13 +280,75 @@ export class MatchApi extends runtime.BaseAPI implements MatchApiInterface { } /** - * Get matches played by authenticated user + * Get normal matches played by authenticated user * Get user matches */ - async getUserMatches( + async getUserNormalMatches( + page?: number, + size?: number, initOverrides?: RequestInit | runtime.InitOverrideFunction, ): Promise> { - const response = await this.getUserMatchesRaw(initOverrides); + const response = await this.getUserNormalMatchesRaw( + { page: page, size: size }, + initOverrides, + ); + return await response.value(); + } + + /** + * Get pvp matches played by authenticated user + * Get user pvp matches + */ + async getUserPvPMatchesRaw( + requestParameters: GetUserPvPMatchesRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise>> { + const queryParameters: any = {}; + + if (requestParameters.page !== undefined) { + queryParameters['page'] = requestParameters.page; + } + + if (requestParameters.size !== undefined) { + queryParameters['size'] = requestParameters.size; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token('http-bearer', []); + + if (tokenString) { + headerParameters['Authorization'] = `Bearer ${tokenString}`; + } + } + const response = await this.request( + { + path: `/user/pvpmatches`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, + initOverrides, + ); + + return new runtime.JSONApiResponse(response); + } + + /** + * Get pvp matches played by authenticated user + * Get user pvp matches + */ + async getUserPvPMatches( + page?: number, + size?: number, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + const response = await this.getUserPvPMatchesRaw( + { page: page, size: size }, + initOverrides, + ); return await response.value(); } } diff --git a/packages/client/src/apis/NotificationApi.ts b/packages/client/src/apis/NotificationApi.ts index ee4bcd2..51b05ac 100644 --- a/packages/client/src/apis/NotificationApi.ts +++ b/packages/client/src/apis/NotificationApi.ts @@ -4,7 +4,7 @@ * CodeCharacter API * Specification of the CodeCharacter API * - * The version of the OpenAPI document: 2023.0.1 + * The version of the OpenAPI document: 2024.0.1 * Contact: delta@nitt.edu * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -13,7 +13,7 @@ */ import * as runtime from '../runtime'; -import type { GenericError, Notification } from '../models'; +import type { GenericError, Notification } from '../models/index'; export interface SaveNotificationReadStatusRequest { notificationId: string; diff --git a/packages/client/src/apis/PvpGameApi.ts b/packages/client/src/apis/PvpGameApi.ts new file mode 100644 index 0000000..b0e3848 --- /dev/null +++ b/packages/client/src/apis/PvpGameApi.ts @@ -0,0 +1,119 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * CodeCharacter API + * Specification of the CodeCharacter API + * + * The version of the OpenAPI document: 2024.0.1 + * Contact: delta@nitt.edu + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import * as runtime from '../runtime'; + +export interface GetPvpGameLogsByGameIdRequest { + gameId: string; +} + +/** + * PvpGameApi - interface + * + * @export + * @interface PvpGameApiInterface + */ +export interface PvpGameApiInterface { + /** + * Get pvp game logs by game ID + * @summary Get pvp game logs by game ID + * @param {string} gameId UUID of the game + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof PvpGameApiInterface + */ + getPvpGameLogsByGameIdRaw( + requestParameters: GetPvpGameLogsByGameIdRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise>; + + /** + * Get pvp game logs by game ID + * Get pvp game logs by game ID + */ + getPvpGameLogsByGameId( + gameId: string, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise; +} + +/** + * + */ +export class PvpGameApi extends runtime.BaseAPI implements PvpGameApiInterface { + /** + * Get pvp game logs by game ID + * Get pvp game logs by game ID + */ + async getPvpGameLogsByGameIdRaw( + requestParameters: GetPvpGameLogsByGameIdRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + if ( + requestParameters.gameId === null || + requestParameters.gameId === undefined + ) { + throw new runtime.RequiredError( + 'gameId', + 'Required parameter requestParameters.gameId was null or undefined when calling getPvpGameLogsByGameId.', + ); + } + + const queryParameters: any = {}; + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token('http-bearer', []); + + if (tokenString) { + headerParameters['Authorization'] = `Bearer ${tokenString}`; + } + } + const response = await this.request( + { + path: `/pvpgames/{gameId}/logs`.replace( + `{${'gameId'}}`, + encodeURIComponent(String(requestParameters.gameId)), + ), + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, + initOverrides, + ); + + if (this.isJsonMime(response.headers.get('content-type'))) { + return new runtime.JSONApiResponse(response); + } else { + return new runtime.TextApiResponse(response) as any; + } + } + + /** + * Get pvp game logs by game ID + * Get pvp game logs by game ID + */ + async getPvpGameLogsByGameId( + gameId: string, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise { + const response = await this.getPvpGameLogsByGameIdRaw( + { gameId: gameId }, + initOverrides, + ); + return await response.value(); + } +} diff --git a/packages/client/src/apis/PvpLeaderboardApi.ts b/packages/client/src/apis/PvpLeaderboardApi.ts new file mode 100644 index 0000000..67ca5e0 --- /dev/null +++ b/packages/client/src/apis/PvpLeaderboardApi.ts @@ -0,0 +1,118 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * CodeCharacter API + * Specification of the CodeCharacter API + * + * The version of the OpenAPI document: 2024.0.1 + * Contact: delta@nitt.edu + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import * as runtime from '../runtime'; +import type { PvPLeaderBoardResponse } from '../models/index'; + +export interface GetPvPLeaderboardRequest { + page?: number; + size?: number; +} + +/** + * PvpLeaderboardApi - interface + * + * @export + * @interface PvpLeaderboardApiInterface + */ +export interface PvpLeaderboardApiInterface { + /** + * Get PvP leaderboard + * @summary Get PvP leaderboard + * @param {number} [page] Index of the page + * @param {number} [size] Size of the page + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof PvpLeaderboardApiInterface + */ + getPvPLeaderboardRaw( + requestParameters: GetPvPLeaderboardRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise>>; + + /** + * Get PvP leaderboard + * Get PvP leaderboard + */ + getPvPLeaderboard( + page?: number, + size?: number, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise>; +} + +/** + * + */ +export class PvpLeaderboardApi + extends runtime.BaseAPI + implements PvpLeaderboardApiInterface +{ + /** + * Get PvP leaderboard + * Get PvP leaderboard + */ + async getPvPLeaderboardRaw( + requestParameters: GetPvPLeaderboardRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise>> { + const queryParameters: any = {}; + + if (requestParameters.page !== undefined) { + queryParameters['page'] = requestParameters.page; + } + + if (requestParameters.size !== undefined) { + queryParameters['size'] = requestParameters.size; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token('http-bearer', []); + + if (tokenString) { + headerParameters['Authorization'] = `Bearer ${tokenString}`; + } + } + const response = await this.request( + { + path: `/pvpleaderboard`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, + initOverrides, + ); + + return new runtime.JSONApiResponse(response); + } + + /** + * Get PvP leaderboard + * Get PvP leaderboard + */ + async getPvPLeaderboard( + page?: number, + size?: number, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + const response = await this.getPvPLeaderboardRaw( + { page: page, size: size }, + initOverrides, + ); + return await response.value(); + } +} diff --git a/packages/client/src/apis/UserApi.ts b/packages/client/src/apis/UserApi.ts index 8ee68e6..356f1c9 100644 --- a/packages/client/src/apis/UserApi.ts +++ b/packages/client/src/apis/UserApi.ts @@ -4,7 +4,7 @@ * CodeCharacter API * Specification of the CodeCharacter API * - * The version of the OpenAPI document: 2023.0.1 + * The version of the OpenAPI document: 2024.0.1 * Contact: delta@nitt.edu * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -18,7 +18,7 @@ import type { GenericError, RatingHistory, RegisterUserRequest, -} from '../models'; +} from '../models/index'; export interface ActivateUserOperationRequest { userId: string; diff --git a/packages/client/src/apis/index.ts b/packages/client/src/apis/index.ts index c469291..c27c6ff 100644 --- a/packages/client/src/apis/index.ts +++ b/packages/client/src/apis/index.ts @@ -9,4 +9,6 @@ export * from './LeaderboardApi'; export * from './MapApi'; export * from './MatchApi'; export * from './NotificationApi'; +export * from './PvpGameApi'; +export * from './PvpLeaderboardApi'; export * from './UserApi'; diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index be9d1ed..bebe8bb 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -1,5 +1,5 @@ /* tslint:disable */ /* eslint-disable */ export * from './runtime'; -export * from './apis'; -export * from './models'; +export * from './apis/index'; +export * from './models/index'; diff --git a/packages/client/src/models/index.ts b/packages/client/src/models/index.ts index e0af0b8..e52e4e5 100644 --- a/packages/client/src/models/index.ts +++ b/packages/client/src/models/index.ts @@ -124,6 +124,7 @@ export interface CodeRevision { export const CodeType = { Normal: 'NORMAL', DailyChallenge: 'DAILY_CHALLENGE', + Pvp: 'PVP', } as const; export type CodeType = (typeof CodeType)[keyof typeof CodeType]; @@ -259,6 +260,12 @@ export interface CreateMatchRequest { * @memberof CreateMatchRequest */ codeRevisionId?: string | null; + /** + * Revision of the code (for SELF-PVP mode) + * @type {string} + * @memberof CreateMatchRequest + */ + codeRevisionId2?: string | null; } /** * Current user profile model @@ -703,6 +710,8 @@ export const MatchMode = { Manual: 'MANUAL', Auto: 'AUTO', Dailychallenge: 'DAILYCHALLENGE', + Pvp: 'PVP', + Selfpvp: 'SELFPVP', } as const; export type MatchMode = (typeof MatchMode)[keyof typeof MatchMode]; @@ -818,6 +827,149 @@ export interface PublicUser { */ avatarId: number; } +/** + * PvP Game model + * @export + * @interface PvPGame + */ +export interface PvPGame { + /** + * + * @type {string} + * @memberof PvPGame + */ + id: string; + /** + * + * @type {number} + * @memberof PvPGame + */ + scorePlayer1: number; + /** + * + * @type {number} + * @memberof PvPGame + */ + scorePlayer2: number; + /** + * + * @type {PvPGameStatus} + * @memberof PvPGame + */ + status: PvPGameStatus; +} + +/** + * + * @export + */ +export const PvPGameStatus = { + Idle: 'IDLE', + Executing: 'EXECUTING', + Executed: 'EXECUTED', + ExecuteError: 'EXECUTE_ERROR', +} as const; +export type PvPGameStatus = (typeof PvPGameStatus)[keyof typeof PvPGameStatus]; + +/** + * Response model for PvP leaderboard + * @export + * @interface PvPLeaderBoardResponse + */ +export interface PvPLeaderBoardResponse { + /** + * + * @type {PublicUser} + * @memberof PvPLeaderBoardResponse + */ + user: PublicUser; + /** + * + * @type {PvPUserStats} + * @memberof PvPLeaderBoardResponse + */ + stats: PvPUserStats; +} +/** + * PvP Match model + * @export + * @interface PvPMatch + */ +export interface PvPMatch { + /** + * + * @type {string} + * @memberof PvPMatch + */ + id: string; + /** + * + * @type {PvPGame} + * @memberof PvPMatch + */ + game: PvPGame; + /** + * + * @type {MatchMode} + * @memberof PvPMatch + */ + matchMode: MatchMode; + /** + * + * @type {Verdict} + * @memberof PvPMatch + */ + matchVerdict: Verdict; + /** + * + * @type {string} + * @memberof PvPMatch + */ + createdAt: string; + /** + * + * @type {PublicUser} + * @memberof PvPMatch + */ + user1: PublicUser; + /** + * + * @type {PublicUser} + * @memberof PvPMatch + */ + user2: PublicUser; +} +/** + * PvP User stats model + * @export + * @interface PvPUserStats + */ +export interface PvPUserStats { + /** + * + * @type {number} + * @memberof PvPUserStats + */ + rating: number; + /** + * + * @type {number} + * @memberof PvPUserStats + */ + wins: number; + /** + * + * @type {number} + * @memberof PvPUserStats + */ + losses: number; + /** + * + * @type {number} + * @memberof PvPUserStats + */ + ties: number; +} /** * Rating history model * @export diff --git a/packages/client/src/runtime.ts b/packages/client/src/runtime.ts index 59bc4e6..2fe6a42 100644 --- a/packages/client/src/runtime.ts +++ b/packages/client/src/runtime.ts @@ -4,7 +4,7 @@ * CodeCharacter API * Specification of the CodeCharacter API * - * The version of the OpenAPI document: 2023.0.1 + * The version of the OpenAPI document: 2024.0.1 * Contact: delta@nitt.edu * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -98,6 +98,10 @@ export const DefaultConfig = new Configuration(); * This is the base class for all generated API classes. */ export class BaseAPI { + private static readonly jsonRegex = new RegExp( + '^(:?application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$', + 'i', + ); private middleware: Middleware[]; constructor(protected configuration = DefaultConfig) { @@ -126,6 +130,23 @@ export class BaseAPI { return this.withMiddleware(...middlewares); } + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + protected isJsonMime(mime: string | null | undefined): boolean { + if (!mime) { + return false; + } + return BaseAPI.jsonRegex.test(mime); + } + protected async request( context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction, @@ -182,14 +203,22 @@ export class BaseAPI { })), }; + let body: any; + if ( + isFormData(overriddenInit.body) || + overriddenInit.body instanceof URLSearchParams || + isBlob(overriddenInit.body) + ) { + body = overriddenInit.body; + } else if (this.isJsonMime(headers['Content-Type'])) { + body = JSON.stringify(overriddenInit.body); + } else { + body = overriddenInit.body; + } + const init: RequestInit = { ...overriddenInit, - body: - isFormData(overriddenInit.body) || - overriddenInit.body instanceof URLSearchParams || - isBlob(overriddenInit.body) - ? overriddenInit.body - : JSON.stringify(overriddenInit.body), + body, }; return { url, init }; diff --git a/packages/client/tsconfig.esm.json b/packages/client/tsconfig.esm.json new file mode 100644 index 0000000..2c0331c --- /dev/null +++ b/packages/client/tsconfig.esm.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "esnext", + "outDir": "dist/esm" + } +} diff --git a/src/assets/codes b/src/assets/codes index 16fbce4..d308b06 160000 --- a/src/assets/codes +++ b/src/assets/codes @@ -1 +1 @@ -Subproject commit 16fbce4e8d3df4ba3dedb1c4622651184ac6adb7 +Subproject commit d308b06259611db71d1b4b6e028c214091f4a86c diff --git a/src/components/Editor/Editor.tsx b/src/components/Editor/Editor.tsx index 6b028af..f9dea07 100644 --- a/src/components/Editor/Editor.tsx +++ b/src/components/Editor/Editor.tsx @@ -32,6 +32,8 @@ import { UserCode, } from '../../store/editor/code'; +import { updatePvPUserCode, PvPUserCode } from '../../store/PvP/pvpCode'; + import { codeCommitIDChanged, codeCommitNameChanged, @@ -40,6 +42,14 @@ import { mapCommitNameChanged, } from '../../store/SelfMatchMakeModal/SelfMatchModal'; +import { + code1CommitIDChanged, + code1CommitNameChanged, + code2CommitIDChanged, + code2CommitNameChanged, + isPvPSelfMatchModalOpened, +} from '../../store/PvPSelfMatchMakeModal/PvPSelfMatchModal'; + import { lspUrl } from '../../config/config'; import { @@ -60,7 +70,9 @@ export default function CodeEditor(props: Editor.Props): JSX.Element { const userCode: string = props.page == 'Dashboard' ? useAppSelector(UserCode) - : useAppSelector(dcCode); + : props.page == 'DailyChallenge' + ? useAppSelector(dcCode) + : useAppSelector(PvPUserCode); const fontSize: number = useAppSelector(FontSize); const theme: string = useAppSelector(Theme); const autocomplete: boolean = useAppSelector(Autocomplete); @@ -158,8 +170,10 @@ export default function CodeEditor(props: Editor.Props): JSX.Element { }; if (props.page == 'Dashboard') { dispatch(updateUserCode(codeNlanguage)); - } else { + } else if (props.page == 'DailyChallenge') { dispatch(changeDcCode(codeNlanguage)); + } else { + dispatch(updatePvPUserCode(codeNlanguage)); } }); @@ -193,6 +207,19 @@ export default function CodeEditor(props: Editor.Props): JSX.Element { ); } + if (props.page == 'PvP') { + editor.addCommand( + monaco.KeyMod.CtrlCmd | monaco.KeyMod.Alt | monaco.KeyCode.KeyN, + function () { + dispatch(isPvPSelfMatchModalOpened(true)); + dispatch(code1CommitNameChanged('Current Player 1 Code')); + dispatch(code1CommitIDChanged(null)); + dispatch(code2CommitNameChanged('Current Player 2 Code')); + dispatch(code2CommitIDChanged(null)); + }, + ); + } + //Keybinding for Submit -> CTRL+SHIFT+S editor.addCommand( diff --git a/src/components/Editor/EditorTypes.ts b/src/components/Editor/EditorTypes.ts index b3cb43b..6cc9b39 100644 --- a/src/components/Editor/EditorTypes.ts +++ b/src/components/Editor/EditorTypes.ts @@ -3,6 +3,7 @@ import { RefObject } from 'react'; interface PageType { Dashboard: 'Dashboard'; DailyChallenge: 'DailyChallenge'; + PvP: 'PvP'; } export type Props = { diff --git a/src/components/EditorInfo/EditorInfo.tsx b/src/components/EditorInfo/EditorInfo.tsx index e0b4dbb..2ae0a36 100644 --- a/src/components/EditorInfo/EditorInfo.tsx +++ b/src/components/EditorInfo/EditorInfo.tsx @@ -26,7 +26,7 @@ const EditorInfo = (): JSX.Element => { onHide={() => dispatch(isInfoOpened(false))} > - {homePageState == 'Dashboard' ? ( + {homePageState == 'Dashboard' || homePageState == 'PvP' ? ( Editor Shortcuts @@ -44,7 +44,7 @@ const EditorInfo = (): JSX.Element => { - {homePageState == 'Dashboard' ? ( + {homePageState == 'Dashboard' || homePageState == 'PvP' ? ( {shortcuts.map((shortcut, index) => ( diff --git a/src/components/Leaderboard/DailyLeaderboard.module.css b/src/components/Leaderboard/DailyLeaderboard.module.css deleted file mode 100644 index 3d3640a..0000000 --- a/src/components/Leaderboard/DailyLeaderboard.module.css +++ /dev/null @@ -1,196 +0,0 @@ -.header { - max-width: 1000px; - margin: 25px auto; - border-radius: 12px; - justify-content: center; - display: flex; - flex-wrap: wrap; - align-items: center; - padding: 10px 25px; - color: #e8ecfd; -} - -.header__icon { - max-width: 50px; - height: auto; - opacity: 0.7; -} - -.header__title { - font-family: 'Roboto Mono', monospace; - font-size: 48px; - text-align: center; - letter-spacing: 4.5px; -} - -.ranklist { - width: 60vw; - display: block; - align-items: center; - padding-top: 10px; - font-family: 'Roboto', sans-serif; - margin: auto; -} - -.list { - margin: 0 auto; - background: transparent; - text-align: center; - vertical-align: middle; - border: none; - padding: 15px; - border-spacing: 0px 20px; - border-collapse: separate; -} - -.item { - border: 1px; - border-style: solid none; - border-radius: 5px; - margin: 8px; -} - -.currentUserItem { - background: #204743; - border-radius: 5px; - margin: 5px; - border: none; -} - -.tierheader { - width: 50px; -} - -.list .item .pos { - text-align: center; - color: #aaaaaa; - font-size: x-large; - border: none; - padding: 15px 30px; -} - -.list .item .pos:first-child { - border-bottom-left-radius: 10px; - border-top-left-radius: 10px; -} - -.list .item .pic { - width: 2rem; - height: 2rem; - align-items: center; - border-radius: 50%; - border: none; -} - -.list .item .name { - flex-basis: 6rem; - font-size: x-large; - text-overflow: ellipsis; - white-space: nowrap; - font-family: 'Roboto', monospace; - overflow: hidden; - color: #aaaaaa; - margin-right: 5px; - border: none; - padding: 15px 30px; -} - -.list .item .score { - font-family: 'Bai Jamjuree', monospace; - color: #aaaaaa; - background-color: transparent; - font-size: x-large; - margin-right: 1.5rem; - opacity: 1; - border: rgba(0, 0, 0, 0.3); - padding: 15px 30px; -} - -.list .item .score:after { - margin-right: 1rem; - opacity: 0.8; - background-color: transparent; - border: none; - padding: 15px; -} - -.list .item .score:last-child { - border-bottom-right-radius: 10px; - border-top-right-radius: 10px; -} - -.list .tableHeader { - font-family: 'Roboto Condensed', monospace; - font-size: x-large; - color: #aaaaaa; - font-weight: 100; - background: transparent; - margin-right: 1.5rem; - opacity: 1; - border: rgba(0, 0, 0, 0.3); - padding: 15px; -} - -.paginationouter { - text-align: center; - flex: 1; -} - -.pagination { - font-family: monospace, sans-serif; - text-decoration: none; - letter-spacing: 2px; - display: inline-flex; - position: relative; - margin-top: 20px; - padding: 10px; - border-radius: 10px; - list-style: none; -} - -.pagination .pageNum { - border-radius: 8px; - color: #aaaaaa; - padding: 8px 15px; - text-decoration: none !important; - text-transform: uppercase; - margin: 5px; - transition: 0.3s; -} - -.pagination .pageNum:hover { - color: #0a1c2a; - background-color: #aaaaaa; - text-decoration: none; - border: 1px; -} - -.pagination .break { - background-color: black; -} - -.pagination .active { - background-color: #2a2a2a; - text-decoration: none; - color: #eaeaea; - border: 1px; -} - -.button { - background-color: #2a2a2a; - color: #eaeaea; - margin: 0px 8px; - border: 1px; - padding: 8px; - text-transform: uppercase; - border-radius: 8px; - font-family: monospace, sans-serif; - display: inline-block; - white-space: nowrap; - transition: 0.3s; -} - -.button:hover { - color: #0a1c2a; - background-color: #aaaaaa; -} diff --git a/src/components/Leaderboard/DailyLeaderboard.tsx b/src/components/Leaderboard/DailyLeaderboard.tsx deleted file mode 100644 index 86d38db..0000000 --- a/src/components/Leaderboard/DailyLeaderboard.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import { useEffect, useState } from 'react'; -import { Table } from 'react-bootstrap'; -import { useAppSelector } from '../../store/hooks'; -import styles from './Leaderboard.module.css'; -import { getAvatarByID } from '../Avatar/Avatar'; -import { - DailyChallengeLeaderBoardResponse, - DailyChallengesApi, -} from '@codecharacter-2024/client'; -import { apiConfig, ApiError } from '../../api/ApiConfig'; -import Loader from '../Loader/Loader'; -import Toast from 'react-hot-toast'; -import { user } from '../../store/User/UserSlice'; - -function PaginatedItems() { - const [page, setPage] = useState(0); - const [items, setItems] = useState([]); - const [nextItems, setNextItems] = useState< - DailyChallengeLeaderBoardResponse[] - >([]); - const [isLoaded, setIsLoaded] = useState(false); - - const itemsPerPage = 8; - const currentUserName = useAppSelector(user).username; - - useEffect(() => { - fetchLeaderboard(page); - }, [page]); - - useEffect(() => { - checkEmpty(); - }, [nextItems]); - - function checkEmpty() { - let emptylistBool = false; - if (nextItems.length == 0) { - emptylistBool = true; - } - return emptylistBool; - } - - const fetchLeaderboard = (pageNum: number) => { - setIsLoaded(false); - const leaderboardAPI = new DailyChallengesApi(apiConfig); - leaderboardAPI - .getDailyChallengeLeaderBoard(pageNum, itemsPerPage) - .then(response => { - setItems(response); - setIsLoaded(true); - }) - .catch(error => { - if (error instanceof ApiError) Toast.error(error.message); - }); - leaderboardAPI - .getDailyChallengeLeaderBoard(pageNum + 1, itemsPerPage) - .then(response => { - setNextItems(response); - }) - .catch(error => { - if (error instanceof ApiError) Toast.error(error.message); - }); - }; - - return ( - <> - <> - {!isLoaded ? ( - - ) : ( - <> -
- - - - - - - - - - {items && - items.map((row: DailyChallengeLeaderBoardResponse) => ( - - - - - - ))} - -
RANKUSERNAMESCORE
- {items.indexOf(row) + 1 + page * itemsPerPage} - -
- - {' ' + row.userName.substring(0, 10)} -
-
{row.score.toFixed(2)}
-
- - )} - - - - ); -} - -export default function DailyChallengeLeaderboard(): JSX.Element { - return ( -
-
-

- Daily Challenge Leaderboard -

-
-
- -
-
- ); -} diff --git a/src/components/Leaderboard/Leaderboard.module.css b/src/components/Leaderboard/Leaderboard.module.css index 1a5fc60..0367efa 100644 --- a/src/components/Leaderboard/Leaderboard.module.css +++ b/src/components/Leaderboard/Leaderboard.module.css @@ -298,4 +298,76 @@ .dropdown-toggle { color: #aaaaaa; background-color: #ffffff; +} + +.selectButton { + position: relative; + color: #ffffff; + width: 190px; + height: 5vh; + margin: 10px; + font-weight: 600; + font-size: 18px; + text-align: center; + font-style: normal; + background-color: inherit; + border: 1px solid white; + border-radius: 5px; + font-family: 'Poppins'; +} + +.selectButton:hover { + transition-duration: 0.5s; + cursor: pointer; +} + +.buttonContainer { + display: flex; + flex-direction: row; + justify-content: center; + margin: 1rem; +} + +.whiteButton { + align-content: right; + color: #ffffff !important; + font-weight: 400; + font-family: Bai Jamjuree; + font-style: normal; + background-color: inherit !important; + font-size: 2em; + border-bottom: 2px solid white; + border-top: 0; + border-left: 0; + border-right: 0; + transition: 0.5s; + border-radius: 0; + padding: 0 50px; +} + +.darkButton { + align-content: right; + color: rgba(234, 234, 234, 0.6); + background-color: inherit !important; + font-family: Bai Jamjuree; + font-weight: 400; + font-style: normal; + border-bottom: 2px solid rgba(234, 234, 234, 0.6); + border-top: 0; + border-left: 0; + border-right: 0; + font-size: 2em; + transition: 0.5s; + border-radius: 0px; + box-shadow: none !important; + padding: 0 50px; +} + +.darkButton:hover { + color: #ffffff; +} + +.whiteButton:active, +.darkButton:active { + box-shadow: none; } \ No newline at end of file diff --git a/src/components/Leaderboard/Leaderboard.tsx b/src/components/Leaderboard/Leaderboard.tsx index 6157ca0..290c6c2 100644 --- a/src/components/Leaderboard/Leaderboard.tsx +++ b/src/components/Leaderboard/Leaderboard.tsx @@ -1,3 +1,4 @@ +import * as LeaderboardType from './LeaderboardTypes'; import { useEffect, useState } from 'react'; import { Modal, Button, Table, Dropdown } from 'react-bootstrap'; import { useAppSelector } from '../../store/hooks'; @@ -9,6 +10,10 @@ import { LeaderboardEntry, MatchMode, TierType, + PvpLeaderboardApi, + PvPLeaderBoardResponse, + DailyChallengeLeaderBoardResponse, + DailyChallengesApi, } from '@codecharacter-2024/client'; import { apiConfig, ApiError } from '../../api/ApiConfig'; import Loader from '../Loader/Loader'; @@ -16,11 +21,23 @@ import swordImage from '../../assets/sword.png'; import Toast from 'react-hot-toast'; import { user } from '../../store/User/UserSlice'; -function PaginatedItems() { +function PaginatedItems(props: LeaderboardType.Props) { const [page, setPage] = useState(0); const [items, setItems] = useState([]); const [nextItems, setNextItems] = useState([]); + const [pvpItems, setPvpItems] = useState([]); + const [pvpNextItems, setPvpNextItems] = useState( + [], + ); + const [dcItems, setDcItems] = useState( + [], + ); + const [dcNextItems, setDcNextItems] = useState< + DailyChallengeLeaderBoardResponse[] + >([]); const [isLoaded, setIsLoaded] = useState(false); + const [isPvPLoaded, setIsPvPLoaded] = useState(false); + const [isDcLoaded, setIsDcLoaded] = useState(false); const [show, setShow] = useState(false); const [currentOpponentUsername, setCurrentOpponentUsername] = useState(''); const [activeTier, setActiveTier] = useState(undefined); @@ -34,13 +51,31 @@ function PaginatedItems() { const itemsPerPage = 8; const currentUserName = useAppSelector(user).username; - useEffect(() => { - fetchLeaderboardByTier(page, activeTier); - }, [page]); - - useEffect(() => { - checkEmpty(); - }, [nextItems]); + switch (props.page) { + case 'PvP': + useEffect(() => { + fetchPvPLeaderboard(page); + }, [page]); + useEffect(() => { + checkPvPEmpty(); + }, [pvpNextItems]); + break; + case 'DailyChallenge': + useEffect(() => { + fetchDcLeaderboard(page); + }, [page]); + useEffect(() => { + checkDcEmpty(); + }, [dcNextItems]); + break; + default: + useEffect(() => { + fetchLeaderboardByTier(page, activeTier); + }, [page]); + useEffect(() => { + checkEmpty(); + }, [nextItems]); + } function checkEmpty() { let emptylistBool = false; @@ -50,6 +85,22 @@ function PaginatedItems() { return emptylistBool; } + function checkPvPEmpty() { + let emptylistBool = false; + if (pvpNextItems.length == 0) { + emptylistBool = true; + } + return emptylistBool; + } + + function checkDcEmpty() { + let emptylistBool = false; + if (dcNextItems.length == 0) { + emptylistBool = true; + } + return emptylistBool; + } + const fetchLeaderboardByTier = (pageNum: number, tier?: TierType) => { setIsLoaded(false); const leaderboardAPI = new LeaderboardApi(apiConfig); @@ -73,6 +124,52 @@ function PaginatedItems() { }); }; + const fetchPvPLeaderboard = (pageNum: number) => { + setIsPvPLoaded(false); + const pvpLeaderBoardApi = new PvpLeaderboardApi(apiConfig); + pvpLeaderBoardApi + .getPvPLeaderboard(pageNum, itemsPerPage) + .then(response => { + setPvpItems(response); + setIsPvPLoaded(true); + }) + .catch(error => { + if (error instanceof ApiError) Toast.error(error.message); + }); + pvpLeaderBoardApi + .getPvPLeaderboard(pageNum + 1, itemsPerPage) + .then(response => { + setPvpNextItems(response); + setIsPvPLoaded(true); + }) + .catch(error => { + if (error instanceof ApiError) Toast.error(error.message); + }); + }; + + const fetchDcLeaderboard = (pageNum: number) => { + setIsDcLoaded(false); + const dcLeaderBoardApi = new DailyChallengesApi(apiConfig); + dcLeaderBoardApi + .getDailyChallengeLeaderBoard(pageNum, itemsPerPage) + .then(response => { + setDcItems(response); + setIsDcLoaded(true); + }) + .catch(error => { + if (error instanceof ApiError) Toast.error(error.message); + }); + dcLeaderBoardApi + .getDailyChallengeLeaderBoard(pageNum + 1, itemsPerPage) + .then(response => { + setDcNextItems(response); + setIsLoaded(true); + }) + .catch(error => { + if (error instanceof ApiError) Toast.error(error.message); + }); + }; + async function handleMatchStart() { const matchAPI = new MatchApi(apiConfig); matchAPI @@ -87,103 +184,266 @@ function PaginatedItems() { }); setShow(false); } + async function handlePvPMatchStart() { + const matchAPI = new MatchApi(apiConfig); + matchAPI + .createMatch({ + mode: MatchMode.Pvp, + opponentUsername: currentOpponentUsername, + codeRevisionId: undefined, + codeRevisionId2: undefined, + }) + .catch(error => { + if (error instanceof ApiError) Toast.error(error.message); + }); + setShow(false); + } return ( <> <> - {!isLoaded ? ( + {props.page == 'Normal' ? ( + !isLoaded ? ( + + ) : ( + <> +
+ + + + Start a new match + + + + + Do you want to start a match against{' '} + {currentOpponentUsername}? + + + + + + + + + + + + + + + + + + + {items && + items.map((row: LeaderboardEntry) => ( + + + + + {currentUserName === row.user.username ? ( + + ) : ( + + )} + + + + + ))} + +
RANKUSERNAMERATINGSWONLOSTTIED
+ {items.indexOf(row) + 1 + page * itemsPerPage} + +
+ + + {' ' + row.user.username.substring(0, 10)} + +
+
+ {row.stats.rating.toFixed(3)} + --- handleShow(row.user.username)} + > + + {row.stats.wins}{row.stats.losses}{row.stats.ties}
+
+ + ) + ) : props.page == 'PvP' ? ( + !isPvPLoaded ? ( + + ) : ( + <> +
+ + + + Start a new PvP match + + + + + Do you want to start a PvP match against{' '} + {currentOpponentUsername}? + + + + + + + + + + + + + + + + + + + {items && + items.map((row: PvPLeaderBoardResponse) => ( + + + + + {currentUserName === row.user.username ? ( + + ) : ( + + )} + + + + + ))} + +
RANKUSERNAMERATINGSWONLOSTTIED
+ {pvpItems.indexOf(row) + 1 + page * itemsPerPage} + +
+ + + {' ' + row.user.username.substring(0, 10)} + +
+
+ {row.stats.rating.toFixed(3)} + --- handleShow(row.user.username)} + > + + {row.stats.wins}{row.stats.losses}{row.stats.ties}
+
+ + ) + ) : !isDcLoaded ? ( ) : ( <>
- - - - Start a new match - - - - - Do you want to start a match against {currentOpponentUsername} - ? - - - - - - - - - - + - {items && - items.map((row: LeaderboardEntry) => ( + {dcItems && + dcItems.map((row: DailyChallengeLeaderBoardResponse) => ( - - {currentUserName === row.user.username ? ( - - ) : ( - - )} - - - + ))} @@ -228,59 +488,58 @@ function PaginatedItems() { > Refresh - - - {activeTier?.toString() || 'All Tiers'} - + {props.page == 'Normal' ? ( + + + {activeTier?.toString() || 'All Tiers'} + - - { - setActiveTier(undefined); - fetchLeaderboardByTier(0); - setPage(0); - }} - > - All Tiers - - { - setActiveTier(TierType.Tier1); - fetchLeaderboardByTier(0, TierType.Tier1); - setPage(0); - }} - > - Tier 1 - - { - setActiveTier(TierType.Tier2); - fetchLeaderboardByTier(0, TierType.Tier2); - setPage(0); - }} - > - Tier 2 - - - + + { + setActiveTier(undefined); + fetchLeaderboardByTier(0); + setPage(0); + }} + > + All Tiers + + { + setActiveTier(TierType.Tier1); + fetchLeaderboardByTier(0, TierType.Tier1); + setPage(0); + }} + > + Tier 1 + + { + setActiveTier(TierType.Tier2); + fetchLeaderboardByTier(0, TierType.Tier2); + setPage(0); + }} + > + Tier 2 + + + + ) : ( + <> + )} ); } -export default function Leaderboard(): JSX.Element { +export default function Leaderboard(props: LeaderboardType.Props): JSX.Element { return (
-
-

- Match Leaderboard -

-
- +
); diff --git a/src/components/Leaderboard/LeaderboardTypes.ts b/src/components/Leaderboard/LeaderboardTypes.ts new file mode 100644 index 0000000..657f8d7 --- /dev/null +++ b/src/components/Leaderboard/LeaderboardTypes.ts @@ -0,0 +1,9 @@ +interface leaderboardType { + Dashboard: 'Normal'; + DailyChallenge: 'DailyChallenge'; + PvP: 'PvP'; +} + +export type Props = { + page: leaderboardType[keyof leaderboardType]; +}; diff --git a/src/components/Leaderboard/MainLeaderboard.tsx b/src/components/Leaderboard/MainLeaderboard.tsx index 6363abc..bf744df 100644 --- a/src/components/Leaderboard/MainLeaderboard.tsx +++ b/src/components/Leaderboard/MainLeaderboard.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import DailyChallengeLeaderboard from './DailyLeaderboard'; +import { Button, ButtonGroup } from 'react-bootstrap'; import Leaderboard from './Leaderboard'; import styles from './Leaderboard.module.css'; import { CurrentUserApi } from '@codecharacter-2024/client'; @@ -8,11 +8,8 @@ import { apiConfig } from '../../api/ApiConfig'; import { dcEnable } from '../../config/config'; export default function BattleTV(): JSX.Element { - const [isDailyChallengeLeaderboard, setIsDailyChallengeLeaderboard] = - useState(false); - const [leaderboardType, setLeaderboardType] = useState( - 'Daily Challenge Leaderboard', - ); + const [leaderboardType, setLeaderboardType] = useState('Normal Leaderboard'); + const [SelectedButton, setSelectedButton] = useState('Normal'); const { setIsOpen } = useTour(); @@ -32,29 +29,97 @@ export default function BattleTV(): JSX.Element { }, []); return (
- {isDailyChallengeLeaderboard ? ( - + {dcEnable ? ( +
+
+ + + + + +
+
) : ( - +
+
+ + + + +
+
)} - {dcEnable ? ( - + {leaderboardType == 'Daily Challenge Leaderboard' ? ( + + ) : leaderboardType == 'PvP Leaderboard' ? ( + ) : ( - <> + )}
); diff --git a/src/components/NavBar/NavBar.tsx b/src/components/NavBar/NavBar.tsx index 02a310d..8ed8d04 100644 --- a/src/components/NavBar/NavBar.tsx +++ b/src/components/NavBar/NavBar.tsx @@ -28,6 +28,7 @@ import { changePageState, changeSimulationState, dailyChallengeCompletionState, + dailyChallengePageState, } from '../../store/DailyChallenge/dailyChallenge'; const NavBar: React.FunctionComponent = () => { @@ -37,6 +38,7 @@ const NavBar: React.FunctionComponent = () => { const loggedInUser = useAppSelector(user); const isLogged = useAppSelector(isloggedIn); const loadingAuth = useAppSelector(loading); + const pageState = useAppSelector(dailyChallengePageState); const dcCompletionstatus = useAppSelector(dailyChallengeCompletionState); useEffect(() => { const cookieValue = document.cookie; @@ -102,6 +104,8 @@ const NavBar: React.FunctionComponent = () => { const [showCompleted, setShowCompleted] = useState(false); const handleCloseCompleted = () => { + dispatch(changePageState('Dashboard')); + navigate('/dashboard', { replace: true }); //Add logic for redirection to view dc leaderboard once done setShowCompleted(false); }; diff --git a/src/components/PvPSelfMatchMakingModal/PvPSelfMatchMakeModal.tsx b/src/components/PvPSelfMatchMakingModal/PvPSelfMatchMakeModal.tsx new file mode 100644 index 0000000..9203dcd --- /dev/null +++ b/src/components/PvPSelfMatchMakingModal/PvPSelfMatchMakeModal.tsx @@ -0,0 +1,201 @@ +import { useEffect, useState } from 'react'; +import styles from '../SelfMatchMakingModal/SelfMatchModalStyle.module.css'; +import Button from 'react-bootstrap/Button'; +import { FormGroup, Col, Container, Row, Modal } from 'react-bootstrap'; +import { useAppDispatch, useAppSelector } from '../../store/hooks'; +import { + isPvPSelfMatchModalOpen, + isPvPSelfMatchModalOpened, + code1CommitID, + code1CommitIDChanged, + code1CommitNameChanged, + code2CommitID, + code2CommitIDChanged, + code2CommitNameChanged, + code1CommitName, + code2CommitName, +} from '../../store/PvPSelfMatchMakeModal/PvPSelfMatchModal'; +import { apiConfig, ApiError } from '../../api/ApiConfig'; +import { + AuthApi, + CodeApi, + CodeRevision, + MatchApi, + MatchMode, +} from '@codecharacter-2024/client'; +import Toast from 'react-hot-toast'; + +const PvPSelfMatchModal = (): JSX.Element => { + const IsPvPSelfMatchModalOpen = useAppSelector(isPvPSelfMatchModalOpen); + const Code1CommitID = useAppSelector(code1CommitID); + const Code2CommitID = useAppSelector(code2CommitID); + const Code1CommitName = useAppSelector(code1CommitName); + const Code2CommitName = useAppSelector(code2CommitName); + const dispatch = useAppDispatch(); + const [completeCodeHistory, setCodeHistory] = useState([]); + + useEffect(() => { + if (localStorage.getItem('token') !== null) { + const authApi = new AuthApi(apiConfig); + authApi + .getAuthStatus() + .then(res => { + const { status } = res; + if (status === 'AUTHENTICATED') { + if (localStorage.getItem('token') != null) { + const codeApi = new CodeApi(apiConfig); + codeApi + .getCodeRevisions() + .then(codeResp => setCodeHistory(codeResp)) + .catch(error => { + if (error instanceof ApiError) Toast.error(error.message); + }); + } + } + }) + .catch((e: Error) => { + if (e instanceof ApiError) { + Toast.error(e.message); + } + }); + } + }, [IsPvPSelfMatchModalOpen]); + + function handleCode1CommitChange(selectedValue: string) { + if (selectedValue === 'Current Player 1 Code') { + dispatch(code1CommitNameChanged('Current Player 1 Code')); + dispatch(code1CommitIDChanged(null)); + } else { + const newCommit = completeCodeHistory.filter( + codeCommit => codeCommit.message === selectedValue, + ); + dispatch(code1CommitNameChanged(newCommit[0].message)); + dispatch(code1CommitIDChanged(newCommit[0].id)); + } + } + + function handleCode2CommitChange(selectedValue: string) { + if (selectedValue === 'Current Player 2 Code') { + dispatch(code2CommitNameChanged('Current Player 2 Code')); + dispatch(code2CommitIDChanged(null)); + } else { + const newCommit = completeCodeHistory.filter( + codeCommit => codeCommit.message === selectedValue, + ); + dispatch(code2CommitNameChanged(newCommit[0].message)); + dispatch(code2CommitIDChanged(newCommit[0].id)); + } + } + + function handleSimulate() { + const matchAPI = new MatchApi(apiConfig); + // figure out how to create pvp match + matchAPI + .createMatch({ + mode: MatchMode.Pvp, + codeRevisionId: Code1CommitID, + codeRevisionId2: Code2CommitID, + }) + .catch(error => { + if (error instanceof ApiError) Toast.error(error.message); + }); + dispatch(isPvPSelfMatchModalOpened(false)); + } + + return ( + dispatch(isPvPSelfMatchModalOpened(false))} + > + + Self Match + + + + + + + + +
+ Code 1 Commit Name +
+ +
+ + + + +
+ Code 2 Commit Name +
+ +
+
+ +
+ + + + + + ); +}; + +export default PvPSelfMatchModal; diff --git a/src/components/SelfMatchMakingModal/SelfMatchMakeModal.tsx b/src/components/SelfMatchMakingModal/SelfMatchMakeModal.tsx index 2c43656..a652910 100644 --- a/src/components/SelfMatchMakingModal/SelfMatchMakeModal.tsx +++ b/src/components/SelfMatchMakingModal/SelfMatchMakeModal.tsx @@ -99,8 +99,8 @@ const selfMatchModal = (): JSX.Element => { } function handleSimulate() { - const mapAPI = new MatchApi(apiConfig); - mapAPI + const matchAPI = new MatchApi(apiConfig); + matchAPI .createMatch({ mode: MatchMode.Self, codeRevisionId: CodeCommitID, diff --git a/src/main.tsx b/src/main.tsx index f84e978..4285c57 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -22,6 +22,9 @@ const EditorSettings = lazy( const SelfMatchModal = lazy( () => import('./components/SelfMatchMakingModal/SelfMatchMakeModal'), ); +const PvPSelfMatchModal = lazy( + () => import('./components/PvPSelfMatchMakingModal/PvPSelfMatchMakeModal'), +); const EditorInfo = lazy(() => import('./components/EditorInfo/EditorInfo')); const CommitModal = lazy(() => import('./components/CommitModal/CommitModal')); @@ -37,6 +40,7 @@ root.render( +
diff --git a/src/pages/Dashboard/Dashboard.module.css b/src/pages/Dashboard/Dashboard.module.css index d83d4a2..6c16ef5 100644 --- a/src/pages/Dashboard/Dashboard.module.css +++ b/src/pages/Dashboard/Dashboard.module.css @@ -110,6 +110,25 @@ box-shadow: none; } +.toolbarButton2 { + font-size: 0.9rem; + width: fit-content; + height: 100%; + border: 0; + margin-left: 5px; + margin-top: 2px; + border-radius: 0.5rem; + outline: none; + background-color: #292929; + color: #ffffff; +} + +.toolbarButton2:focus { + font-size: 0.9rem; + outline: none; + box-shadow: none; +} + .settingDropdown { margin-top: 5 px; } diff --git a/src/pages/Dashboard/Dashboard.tsx b/src/pages/Dashboard/Dashboard.tsx index 072c263..cd1a127 100644 --- a/src/pages/Dashboard/Dashboard.tsx +++ b/src/pages/Dashboard/Dashboard.tsx @@ -41,6 +41,13 @@ import { mapCommitIDChanged, mapCommitNameChanged, } from '../../store/SelfMatchMakeModal/SelfMatchModal'; +import { + code1CommitIDChanged, + code1CommitNameChanged, + code2CommitIDChanged, + code2CommitNameChanged, + isPvPSelfMatchModalOpened, +} from '../../store/PvPSelfMatchMakeModal/PvPSelfMatchModal'; import { loggedIn, user } from '../../store/User/UserSlice'; import { @@ -61,8 +68,13 @@ import { dcCodeLanguage, dcCode, dcSimulation, + changePageState, } from '../../store/DailyChallenge/dailyChallenge'; - +import { + changePvPLanguage, + PvPUserCode, + PvPUserLanguage, +} from '../../store/PvP/pvpCode'; import Tour from '../../components/TourProvider/TourProvider'; import { EditorSteps } from '../../components/TourProvider/EditorSteps'; import { useNavigate } from 'react-router-dom'; @@ -118,6 +130,7 @@ export default function Dashboard(): JSX.Element { const userCode = useAppSelector(UserCode); const dailyChallengeCode = useAppSelector(dcCode); + const pvpCode = useAppSelector(PvPUserCode); const dispatch = useAppDispatch(); const dailyChallenge = useAppSelector(dailyChallengeState); const pageState = useAppSelector(dailyChallengePageState); @@ -125,7 +138,9 @@ export default function Dashboard(): JSX.Element { const userLanguage = pageState == 'Dashboard' ? useAppSelector(UserLanguage) - : useAppSelector(dcCodeLanguage); + : pageState == 'DailyChallenge' + ? useAppSelector(dcCodeLanguage) + : useAppSelector(PvPUserLanguage); const codeAPI = new CodeApi(apiConfig); const dailyChallengeAPI = new DailyChallengesApi(apiConfig); @@ -168,6 +183,7 @@ export default function Dashboard(): JSX.Element { }, []); const languages: string[] = ['C++', 'Python', 'Java']; + const modes: string[] = ['Normal', 'PvP']; const localStoreLanguageChose = localStorage.getItem('languageChose'); const [languageChose, setLanguageChose] = useState( @@ -175,32 +191,63 @@ export default function Dashboard(): JSX.Element { ); const handleLanguageChange = (language: string) => { + console.log(language); switch (language) { case 'C++': - pageState == 'Dashboard' - ? dispatch(changeLanguage('c_cpp')) - : dispatch(changeDcLanguage('c_cpp')); + console.log('changed to cpp'); + console.log(pageState); + switch (pageState) { + case 'Dashboard': + dispatch(changeLanguage('c_cpp')); + break; + case 'DailyChallenge': + dispatch(changeDcLanguage('c_cpp')); + break; + case 'PvP': + console.log('it came to pvp too'); + dispatch(changePvPLanguage('c_cpp')); + break; + default: + dispatch(changeLanguage('c_cpp')); + } setLanguageChose('C++'); localStorage.setItem('languageChose', 'C++'); break; case 'Python': + console.log('here'); pageState == 'Dashboard' ? dispatch(changeLanguage('python')) - : dispatch(changeDcLanguage('python')); + : pageState == 'DailyChallenge' + ? dispatch(changeDcLanguage('python')) + : dispatch(changePvPLanguage('python')); setLanguageChose('Python'); localStorage.setItem('languageChose', 'Python'); break; case 'Java': pageState == 'Dashboard' ? dispatch(changeLanguage('java')) - : dispatch(changeDcLanguage('java')); + : pageState == 'DailyChallenge' + ? dispatch(changeDcLanguage('java')) + : dispatch(changePvPLanguage('java')); setLanguageChose('Java'); localStorage.setItem('languageChose', 'Java'); break; default: - dispatch(changeLanguage('c_cpp')); + pageState == 'Dashboard' + ? dispatch(changeLanguage('c_cpp')) + : pageState == 'DailyChallenge' + ? dispatch(changeDcLanguage('c_cpp')) + : dispatch(changePvPLanguage('c_cpp')); } }; + const handlePvPTake = () => { + dispatch(changePageState('PvP')); + navigate('/dashboard', { replace: true }); + }; + const handlePvPClose = () => { + dispatch(changePageState('Dashboard')); + navigate('/dashboard'); + }; const handleSave = () => { let languageType: Language = Language.Cpp; @@ -210,8 +257,18 @@ export default function Dashboard(): JSX.Element { codeAPI .updateLatestCode({ - codeType: pageState == 'Dashboard' ? 'NORMAL' : 'DAILY_CHALLENGE', - code: pageState == 'Dashboard' ? userCode : dailyChallengeCode, + codeType: + pageState == 'Dashboard' + ? 'NORMAL' + : pageState == 'DailyChallenge' + ? 'DAILY_CHALLENGE' + : 'PVP', + code: + pageState == 'Dashboard' + ? userCode + : pageState == 'DailyChallenge' + ? dailyChallengeCode + : pvpCode, lock: false, language: languageType, }) @@ -231,6 +288,14 @@ export default function Dashboard(): JSX.Element { dispatch(mapCommitIDChanged(null)); } + function handlePvPSimulate() { + dispatch(isPvPSelfMatchModalOpened(true)); + dispatch(code1CommitNameChanged('Current Player 1 Code')); + dispatch(code1CommitIDChanged(null)); + dispatch(code2CommitNameChanged('Current Player 2 Code')); + dispatch(code2CommitIDChanged(null)); + } + const isCommitModalOpen = useAppSelector(IsCommitModalOpen); function handleOpenCommitModal() { @@ -260,13 +325,23 @@ export default function Dashboard(): JSX.Element { codeAPI .updateLatestCode({ - codeType: pageState == 'Dashboard' ? 'NORMAL' : 'DAILY_CHALLENGE', - code: pageState == 'Dashboard' ? userCode : dailyChallengeCode, + codeType: + pageState == 'Dashboard' + ? 'NORMAL' + : pageState == 'DailyChallenge' + ? 'DAILY_CHALLENGE' + : 'PVP', + code: + pageState == 'Dashboard' + ? userCode + : pageState == 'DailyChallenge' + ? dailyChallengeCode + : pvpCode, lock: true, language: languageType, }) .then(() => { - if (pageState == 'Dashboard') { + if (pageState == 'Dashboard' || pageState == 'PvP') { Toast.success('Code Submitted'); } }) @@ -333,7 +408,9 @@ export default function Dashboard(): JSX.Element { minSize={520} >
- {pageState == 'Dashboard' || dailyChallenge.challType == 'MAP' ? ( + {pageState == 'Dashboard' || + pageState == 'PvP' || + dailyChallenge.challType == 'MAP' ? (
+ + + + + + ) : ( <> )} @@ -430,6 +537,24 @@ export default function Dashboard(): JSX.Element { + + + e.target.value == 'PvP' + ? handlePvPTake() + : handlePvPClose() + } + id="ModeSelector" + > + {modes.map(mode => ( + + ))} + +
@@ -513,6 +638,7 @@ export default function Dashboard(): JSX.Element { )}
{pageState == 'Dashboard' || + pageState == 'PvP' || dailyChallenge.challType == 'MAP' ? (
- {pageState == 'Dashboard' || dailyChallengeSimulationState ? ( + {pageState == 'Dashboard' || + pageState == 'PvP' || + dailyChallengeSimulationState ? ( ) : dailyChallenge.challType == 'MAP' ? ( <> @@ -583,7 +715,9 @@ export default function Dashboard(): JSX.Element { )}
- {pageState == 'Dashboard' || dailyChallengeSimulationState ? ( + {pageState == 'Dashboard' || + pageState == 'PvP' || + dailyChallengeSimulationState ? ( ) : ( <> diff --git a/src/store/DailyChallenge/dailyChallenge.ts b/src/store/DailyChallenge/dailyChallenge.ts index c839e46..93cc68e 100644 --- a/src/store/DailyChallenge/dailyChallenge.ts +++ b/src/store/DailyChallenge/dailyChallenge.ts @@ -11,7 +11,7 @@ import defaultJavaCode from '../../assets/codes/java/Run.java?raw'; export interface DailyChallengeStateType { dailyChallenge: DailyChallengeGetRequest; - pageType: 'Dashboard' | 'DailyChallenge'; + pageType: 'Dashboard' | 'DailyChallenge' | 'PvP'; dcCode: string; dcAllLanguagesCode: string[]; codeLanguage: string; @@ -64,7 +64,7 @@ export const dailyChallengeSlice = createSlice({ }, changePageState: ( state, - action: PayloadAction<'Dashboard' | 'DailyChallenge'>, + action: PayloadAction<'Dashboard' | 'DailyChallenge' | 'PvP'>, ) => { state.pageType = action.payload; }, @@ -110,7 +110,7 @@ export const dailyChallengeState = ( ): DailyChallengeGetRequest => state.dailyChallenge.dailyChallenge; export const dailyChallengePageState = ( state: RootState, -): 'Dashboard' | 'DailyChallenge' => state.dailyChallenge.pageType; +): 'Dashboard' | 'DailyChallenge' | 'PvP' => state.dailyChallenge.pageType; export const dailyChallengeCompletionState = ( state: RootState, ): boolean | undefined => state.dailyChallenge.dailyChallenge.completionStatus; diff --git a/src/store/PvP/pvpCode.ts b/src/store/PvP/pvpCode.ts new file mode 100644 index 0000000..8e9d084 --- /dev/null +++ b/src/store/PvP/pvpCode.ts @@ -0,0 +1,77 @@ +import { Code } from '@codecharacter-2024/client'; +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { RootState } from '../store'; +import defaultCppCode from '../../assets/codes/cpp/runpvp.cpp?raw'; +import defaultPythonCode from '../../assets/codes/python/runpvp.py?raw'; +import defaultJavaCode from '../../assets/codes/java/RunPvP.java?raw'; + +export const languagesAvailable = ['c_cpp', 'python', 'java']; + +export interface pvpEditorStateType { + pvpAllLanguagesCode: string[]; + pvpUserCode: string; + pvpLanguage: string; + pvpLastSavedAt: Date; +} + +const initialState: pvpEditorStateType = { + pvpAllLanguagesCode: [defaultCppCode, defaultPythonCode, defaultJavaCode], + pvpUserCode: '', + pvpLanguage: 'c_cpp', + pvpLastSavedAt: new Date(), +}; + +export interface PvPCodeAndLanguage { + currentUserCode: string; + currentUserLanguage: string; +} + +export const pvpEditorSlice = createSlice({ + name: 'pvpEditorState', + initialState: initialState, + reducers: { + initializePvPEditorStates: (state, action: PayloadAction) => { + state.pvpUserCode = action.payload.code; + if (action.payload.language === 'C' || action.payload.language === 'CPP') + state.pvpLanguage = 'c_cpp'; + else if (action.payload.language === 'PYTHON') + state.pvpLanguage = 'python'; + else if (action.payload.language === 'JAVA') state.pvpLanguage = 'java'; + state.pvpLastSavedAt = action.payload.lastSavedAt; + const desiredIndex = languagesAvailable.indexOf(state.pvpLanguage); + state.pvpAllLanguagesCode[desiredIndex] = action.payload.code; + }, + + updatePvPUserCode: (state, action: PayloadAction) => { + const tempCurrentUserLanguage = action.payload.currentUserLanguage; + const desiredIndex = languagesAvailable.indexOf(tempCurrentUserLanguage); + const newCodeAndLanguage: PvPCodeAndLanguage = { + currentUserCode: action.payload.currentUserCode, + currentUserLanguage: action.payload.currentUserLanguage, + }; + + state.pvpAllLanguagesCode[desiredIndex] = + newCodeAndLanguage.currentUserCode; + state.pvpUserCode = newCodeAndLanguage.currentUserCode; + }, + + changePvPLanguage: (state, action: PayloadAction) => { + console.log('lang changed'); + const tempCurrentUserLanguage = action.payload; + const desiredIndex = languagesAvailable.indexOf(tempCurrentUserLanguage); + state.pvpUserCode = state.pvpAllLanguagesCode[desiredIndex]; + state.pvpLanguage = action.payload; + }, + }, +}); + +export const { + updatePvPUserCode, + changePvPLanguage, + initializePvPEditorStates, +} = pvpEditorSlice.actions; +export const PvPUserCode = (state: RootState): string => + state.pvpEditorReducer.pvpUserCode; +export const PvPUserLanguage = (state: RootState): string => + state.pvpEditorReducer.pvpLanguage; +export default pvpEditorSlice.reducer; diff --git a/src/store/PvPSelfMatchMakeModal/PvPSelfMatchModal.ts b/src/store/PvPSelfMatchMakeModal/PvPSelfMatchModal.ts new file mode 100644 index 0000000..aa20542 --- /dev/null +++ b/src/store/PvPSelfMatchMakeModal/PvPSelfMatchModal.ts @@ -0,0 +1,61 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { RootState } from '../store'; + +export interface PvPSelfMatchModalState { + isPvPSelfMatchModalOpen: boolean; + code1CommitID: string | null; + code2CommitID: string | null; + code1CommitName: string; + code2CommitName: string; +} + +const initialState: PvPSelfMatchModalState = { + isPvPSelfMatchModalOpen: false, + code1CommitID: null, + code2CommitID: null, + code1CommitName: 'Current Player 1 Code', + code2CommitName: 'Current Player 2 Code', +}; + +export const PvPSelfMatchModalSlice = createSlice({ + name: 'PvPSelfMatchModalState', + initialState, + reducers: { + isPvPSelfMatchModalOpened: (state, action: PayloadAction) => { + state.isPvPSelfMatchModalOpen = action.payload; + }, + code1CommitIDChanged: (state, action: PayloadAction) => { + state.code1CommitID = action.payload; + }, + code2CommitIDChanged: (state, action: PayloadAction) => { + state.code2CommitID = action.payload; + }, + code1CommitNameChanged: (state, action: PayloadAction) => { + state.code1CommitName = action.payload; + }, + code2CommitNameChanged: (state, action: PayloadAction) => { + state.code2CommitName = action.payload; + }, + }, +}); + +export const { + isPvPSelfMatchModalOpened, + code1CommitIDChanged, + code2CommitIDChanged, + code1CommitNameChanged, + code2CommitNameChanged, +} = PvPSelfMatchModalSlice.actions; + +export const isPvPSelfMatchModalOpen = (state: RootState): boolean => + state.pvpSelfMatchModal.isPvPSelfMatchModalOpen; +export const code1CommitID = (state: RootState): string | null => + state.pvpSelfMatchModal.code1CommitID; +export const code2CommitID = (state: RootState): string | null => + state.pvpSelfMatchModal.code2CommitID; +export const code1CommitName = (state: RootState): string => + state.pvpSelfMatchModal.code1CommitName; +export const code2CommitName = (state: RootState): string => + state.pvpSelfMatchModal.code2CommitName; + +export default PvPSelfMatchModalSlice.reducer; diff --git a/src/store/store.ts b/src/store/store.ts index 907bf97..b00bb4c 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -12,7 +12,9 @@ import editorReducer from './editor/code'; import settingsReducer from './EditorSettings/settings'; import logReducer from './rendererLogs/logSlice'; import selfMatchModalReducer from './SelfMatchMakeModal/SelfMatchModal'; +import PvPSelfMatchModalReducer from './PvPSelfMatchMakeModal/PvPSelfMatchModal'; import dailyChallengeReducer from './DailyChallenge/dailyChallenge'; +import pvpCodeReducer from './PvP/pvpCode'; const reducers = combineReducers({ editorState: editorReducer, @@ -35,7 +37,9 @@ export const store = configureStore({ codeEditorReducer: persistedReducer, logs: logReducer, selfMatchModal: selfMatchModalReducer, + pvpSelfMatchModal: PvPSelfMatchModalReducer, dailyChallenge: dailyChallengeReducer, + pvpEditorReducer: pvpCodeReducer, }, middleware: [thunk], }); From ce6068e9049f18b09a23e5438af0d7ade8c9a10c Mon Sep 17 00:00:00 2001 From: shubham-1806 Date: Sun, 4 Feb 2024 18:32:15 +0530 Subject: [PATCH 2/8] feat: editor redux update, wip phaser pvp --- docs/spec/CodeCharacter-API.yml | 13 +- packages/client/.openapi-generator/FILES | 1 - packages/client/.openapi-generator/VERSION | 2 +- packages/client/src/apis/AuthApi.ts | 2 +- packages/client/src/apis/CodeApi.ts | 2 +- packages/client/src/apis/CurrentUserApi.ts | 2 +- .../client/src/apis/DailyChallengesApi.ts | 2 +- packages/client/src/apis/GameApi.ts | 6 +- packages/client/src/apis/LeaderboardApi.ts | 92 +++++++++- packages/client/src/apis/MapApi.ts | 2 +- packages/client/src/apis/MatchApi.ts | 2 +- packages/client/src/apis/NotificationApi.ts | 2 +- packages/client/src/apis/PvpGameApi.ts | 6 +- packages/client/src/apis/PvpLeaderboardApi.ts | 118 ------------- packages/client/src/apis/UserApi.ts | 2 +- packages/client/src/apis/index.ts | 1 - packages/client/src/index.ts | 4 +- packages/client/src/runtime.ts | 41 +---- packages/renderer/src/events/EventEmitter.ts | 2 + packages/renderer/src/parser/Instructions.ts | 9 +- packages/renderer/src/parser/Parser.ts | 6 +- packages/renderer/src/scenes/TileMap.ts | 160 +++++++++++++---- src/components/CommitModal/CommitModal.tsx | 7 +- src/components/Editor/Editor.tsx | 4 - src/components/Leaderboard/Leaderboard.tsx | 17 +- .../PvPSelfMatchMakeModal.tsx | 5 +- src/components/Websocket/Websocket.tsx | 6 +- src/pages/Dashboard/Dashboard.tsx | 163 ++++++------------ src/store/DailyChallenge/dailyChallenge.ts | 6 +- src/store/PvP/pvpCode.ts | 77 --------- src/store/editor/code.ts | 73 +++++++- src/store/rendererLogs/logAPI.ts | 40 +++-- src/store/rendererLogs/logSlice.ts | 9 +- src/store/store.ts | 2 - 34 files changed, 431 insertions(+), 455 deletions(-) delete mode 100644 packages/client/src/apis/PvpLeaderboardApi.ts delete mode 100644 src/store/PvP/pvpCode.ts diff --git a/docs/spec/CodeCharacter-API.yml b/docs/spec/CodeCharacter-API.yml index 8c91af0..98330f6 100644 --- a/docs/spec/CodeCharacter-API.yml +++ b/docs/spec/CodeCharacter-API.yml @@ -332,7 +332,6 @@ paths: rating: 500 wins: 6 losses: 0 - ties: 1 - user: username: testUser2 name: >- @@ -345,7 +344,6 @@ paths: rating: 50 wins: 3 losses: 3 - ties: 0 - user: username: testUser3 name: >- @@ -358,7 +356,6 @@ paths: rating: 80 wins: 1 losses: 0 - ties: 0 - user: username: testUser4 name: Test User4 @@ -369,7 +366,6 @@ paths: rating: 230 wins: 0 losses: 0 - ties: 3 - user: username: testUser5 name: Test User5 Test Test @@ -380,7 +376,6 @@ paths: rating: 850 wins: 0 losses: 0 - ties: 4 - user: username: testUser6 name: >- @@ -393,7 +388,6 @@ paths: rating: 600 wins: 0 losses: 0 - ties: 2 - user: username: testUser7 name: >- @@ -406,7 +400,6 @@ paths: rating: 57 wins: 20 losses: 70 - ties: 1 - user: username: testUser8 name: Test User8 @@ -417,7 +410,6 @@ paths: rating: 850 wins: 0 losses: 0 - ties: 0 - user: username: testUser9 name: Test User9 @@ -428,7 +420,6 @@ paths: rating: 78 wins: 0 losses: 0 - ties: 4 - user: username: testUser10 name: Test User10 @@ -439,12 +430,11 @@ paths: rating: 10 wins: 0 losses: 0 - ties: 2 '401': description: Unauthorized operationId: getPvPLeaderboard tags: - - pvpLeaderboard + - leaderboard parameters: - schema: type: integer @@ -2307,7 +2297,6 @@ components: stats: $ref: '#/components/schemas/UserStats' required: - - user - user - stats description: Leaderboard entry model diff --git a/packages/client/.openapi-generator/FILES b/packages/client/.openapi-generator/FILES index 53a949a..2b446d7 100644 --- a/packages/client/.openapi-generator/FILES +++ b/packages/client/.openapi-generator/FILES @@ -9,7 +9,6 @@ src/apis/MapApi.ts src/apis/MatchApi.ts src/apis/NotificationApi.ts src/apis/PvpGameApi.ts -src/apis/PvpLeaderboardApi.ts src/apis/UserApi.ts src/apis/index.ts src/index.ts diff --git a/packages/client/.openapi-generator/VERSION b/packages/client/.openapi-generator/VERSION index 4b49d9b..e7e42a4 100644 --- a/packages/client/.openapi-generator/VERSION +++ b/packages/client/.openapi-generator/VERSION @@ -1 +1 @@ -7.2.0 \ No newline at end of file +6.3.0 \ No newline at end of file diff --git a/packages/client/src/apis/AuthApi.ts b/packages/client/src/apis/AuthApi.ts index 39e434f..7a00a4a 100644 --- a/packages/client/src/apis/AuthApi.ts +++ b/packages/client/src/apis/AuthApi.ts @@ -20,7 +20,7 @@ import type { PasswordLoginRequest, PasswordLoginResponse, ResetPasswordRequest, -} from '../models/index'; +} from '../models'; export interface ForgotPasswordOperationRequest { forgotPasswordRequest: ForgotPasswordRequest; diff --git a/packages/client/src/apis/CodeApi.ts b/packages/client/src/apis/CodeApi.ts index b9db2b5..60d0253 100644 --- a/packages/client/src/apis/CodeApi.ts +++ b/packages/client/src/apis/CodeApi.ts @@ -20,7 +20,7 @@ import type { CreateCodeRevisionRequest, GenericError, UpdateLatestCodeRequest, -} from '../models/index'; +} from '../models'; export interface CreateCodeRevisionOperationRequest { createCodeRevisionRequest: CreateCodeRevisionRequest; diff --git a/packages/client/src/apis/CurrentUserApi.ts b/packages/client/src/apis/CurrentUserApi.ts index 7589b71..2ec3282 100644 --- a/packages/client/src/apis/CurrentUserApi.ts +++ b/packages/client/src/apis/CurrentUserApi.ts @@ -19,7 +19,7 @@ import type { GenericError, UpdateCurrentUserProfile, UpdatePasswordRequest, -} from '../models/index'; +} from '../models'; export interface CompleteUserProfileRequest { completeProfileRequest: CompleteProfileRequest; diff --git a/packages/client/src/apis/DailyChallengesApi.ts b/packages/client/src/apis/DailyChallengesApi.ts index d039910..56d4d19 100644 --- a/packages/client/src/apis/DailyChallengesApi.ts +++ b/packages/client/src/apis/DailyChallengesApi.ts @@ -19,7 +19,7 @@ import type { DailyChallengeMatchRequest, GenericError, Match, -} from '../models/index'; +} from '../models'; export interface CreateDailyChallengeMatchRequest { dailyChallengeMatchRequest: DailyChallengeMatchRequest; diff --git a/packages/client/src/apis/GameApi.ts b/packages/client/src/apis/GameApi.ts index 75e567d..68e2885 100644 --- a/packages/client/src/apis/GameApi.ts +++ b/packages/client/src/apis/GameApi.ts @@ -95,11 +95,7 @@ export class GameApi extends runtime.BaseAPI implements GameApiInterface { initOverrides, ); - if (this.isJsonMime(response.headers.get('content-type'))) { - return new runtime.JSONApiResponse(response); - } else { - return new runtime.TextApiResponse(response) as any; - } + return new runtime.TextApiResponse(response) as any; } /** diff --git a/packages/client/src/apis/LeaderboardApi.ts b/packages/client/src/apis/LeaderboardApi.ts index d8a11d6..9ea2c4c 100644 --- a/packages/client/src/apis/LeaderboardApi.ts +++ b/packages/client/src/apis/LeaderboardApi.ts @@ -13,7 +13,11 @@ */ import * as runtime from '../runtime'; -import type { LeaderboardEntry, TierType } from '../models/index'; +import type { + LeaderboardEntry, + PvPLeaderBoardResponse, + TierType, +} from '../models'; export interface GetLeaderboardRequest { page?: number; @@ -21,6 +25,11 @@ export interface GetLeaderboardRequest { tier?: TierType; } +export interface GetPvPLeaderboardRequest { + page?: number; + size?: number; +} + /** * LeaderboardApi - interface * @@ -53,6 +62,30 @@ export interface LeaderboardApiInterface { tier?: TierType, initOverrides?: RequestInit | runtime.InitOverrideFunction, ): Promise>; + + /** + * Get PvP leaderboard + * @summary Get PvP leaderboard + * @param {number} [page] Index of the page + * @param {number} [size] Size of the page + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof LeaderboardApiInterface + */ + getPvPLeaderboardRaw( + requestParameters: GetPvPLeaderboardRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise>>; + + /** + * Get PvP leaderboard + * Get PvP leaderboard + */ + getPvPLeaderboard( + page?: number, + size?: number, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise>; } /** @@ -123,4 +156,61 @@ export class LeaderboardApi ); return await response.value(); } + + /** + * Get PvP leaderboard + * Get PvP leaderboard + */ + async getPvPLeaderboardRaw( + requestParameters: GetPvPLeaderboardRequest, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise>> { + const queryParameters: any = {}; + + if (requestParameters.page !== undefined) { + queryParameters['page'] = requestParameters.page; + } + + if (requestParameters.size !== undefined) { + queryParameters['size'] = requestParameters.size; + } + + const headerParameters: runtime.HTTPHeaders = {}; + + if (this.configuration && this.configuration.accessToken) { + const token = this.configuration.accessToken; + const tokenString = await token('http-bearer', []); + + if (tokenString) { + headerParameters['Authorization'] = `Bearer ${tokenString}`; + } + } + const response = await this.request( + { + path: `/pvpleaderboard`, + method: 'GET', + headers: headerParameters, + query: queryParameters, + }, + initOverrides, + ); + + return new runtime.JSONApiResponse(response); + } + + /** + * Get PvP leaderboard + * Get PvP leaderboard + */ + async getPvPLeaderboard( + page?: number, + size?: number, + initOverrides?: RequestInit | runtime.InitOverrideFunction, + ): Promise> { + const response = await this.getPvPLeaderboardRaw( + { page: page, size: size }, + initOverrides, + ); + return await response.value(); + } } diff --git a/packages/client/src/apis/MapApi.ts b/packages/client/src/apis/MapApi.ts index d038e6e..7680341 100644 --- a/packages/client/src/apis/MapApi.ts +++ b/packages/client/src/apis/MapApi.ts @@ -21,7 +21,7 @@ import type { GenericError, MapCommitByCommitIdResponse, UpdateLatestMapRequest, -} from '../models/index'; +} from '../models'; export interface CreateMapRevisionOperationRequest { createMapRevisionRequest: CreateMapRevisionRequest; diff --git a/packages/client/src/apis/MatchApi.ts b/packages/client/src/apis/MatchApi.ts index 483d6e0..40f284d 100644 --- a/packages/client/src/apis/MatchApi.ts +++ b/packages/client/src/apis/MatchApi.ts @@ -18,7 +18,7 @@ import type { GenericError, Match, PvPMatch, -} from '../models/index'; +} from '../models'; export interface CreateMatchOperationRequest { createMatchRequest: CreateMatchRequest; diff --git a/packages/client/src/apis/NotificationApi.ts b/packages/client/src/apis/NotificationApi.ts index 51b05ac..437ae14 100644 --- a/packages/client/src/apis/NotificationApi.ts +++ b/packages/client/src/apis/NotificationApi.ts @@ -13,7 +13,7 @@ */ import * as runtime from '../runtime'; -import type { GenericError, Notification } from '../models/index'; +import type { GenericError, Notification } from '../models'; export interface SaveNotificationReadStatusRequest { notificationId: string; diff --git a/packages/client/src/apis/PvpGameApi.ts b/packages/client/src/apis/PvpGameApi.ts index b0e3848..8a5261d 100644 --- a/packages/client/src/apis/PvpGameApi.ts +++ b/packages/client/src/apis/PvpGameApi.ts @@ -95,11 +95,7 @@ export class PvpGameApi extends runtime.BaseAPI implements PvpGameApiInterface { initOverrides, ); - if (this.isJsonMime(response.headers.get('content-type'))) { - return new runtime.JSONApiResponse(response); - } else { - return new runtime.TextApiResponse(response) as any; - } + return new runtime.TextApiResponse(response) as any; } /** diff --git a/packages/client/src/apis/PvpLeaderboardApi.ts b/packages/client/src/apis/PvpLeaderboardApi.ts deleted file mode 100644 index 67ca5e0..0000000 --- a/packages/client/src/apis/PvpLeaderboardApi.ts +++ /dev/null @@ -1,118 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -/** - * CodeCharacter API - * Specification of the CodeCharacter API - * - * The version of the OpenAPI document: 2024.0.1 - * Contact: delta@nitt.edu - * - * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). - * https://openapi-generator.tech - * Do not edit the class manually. - */ - -import * as runtime from '../runtime'; -import type { PvPLeaderBoardResponse } from '../models/index'; - -export interface GetPvPLeaderboardRequest { - page?: number; - size?: number; -} - -/** - * PvpLeaderboardApi - interface - * - * @export - * @interface PvpLeaderboardApiInterface - */ -export interface PvpLeaderboardApiInterface { - /** - * Get PvP leaderboard - * @summary Get PvP leaderboard - * @param {number} [page] Index of the page - * @param {number} [size] Size of the page - * @param {*} [options] Override http request option. - * @throws {RequiredError} - * @memberof PvpLeaderboardApiInterface - */ - getPvPLeaderboardRaw( - requestParameters: GetPvPLeaderboardRequest, - initOverrides?: RequestInit | runtime.InitOverrideFunction, - ): Promise>>; - - /** - * Get PvP leaderboard - * Get PvP leaderboard - */ - getPvPLeaderboard( - page?: number, - size?: number, - initOverrides?: RequestInit | runtime.InitOverrideFunction, - ): Promise>; -} - -/** - * - */ -export class PvpLeaderboardApi - extends runtime.BaseAPI - implements PvpLeaderboardApiInterface -{ - /** - * Get PvP leaderboard - * Get PvP leaderboard - */ - async getPvPLeaderboardRaw( - requestParameters: GetPvPLeaderboardRequest, - initOverrides?: RequestInit | runtime.InitOverrideFunction, - ): Promise>> { - const queryParameters: any = {}; - - if (requestParameters.page !== undefined) { - queryParameters['page'] = requestParameters.page; - } - - if (requestParameters.size !== undefined) { - queryParameters['size'] = requestParameters.size; - } - - const headerParameters: runtime.HTTPHeaders = {}; - - if (this.configuration && this.configuration.accessToken) { - const token = this.configuration.accessToken; - const tokenString = await token('http-bearer', []); - - if (tokenString) { - headerParameters['Authorization'] = `Bearer ${tokenString}`; - } - } - const response = await this.request( - { - path: `/pvpleaderboard`, - method: 'GET', - headers: headerParameters, - query: queryParameters, - }, - initOverrides, - ); - - return new runtime.JSONApiResponse(response); - } - - /** - * Get PvP leaderboard - * Get PvP leaderboard - */ - async getPvPLeaderboard( - page?: number, - size?: number, - initOverrides?: RequestInit | runtime.InitOverrideFunction, - ): Promise> { - const response = await this.getPvPLeaderboardRaw( - { page: page, size: size }, - initOverrides, - ); - return await response.value(); - } -} diff --git a/packages/client/src/apis/UserApi.ts b/packages/client/src/apis/UserApi.ts index 356f1c9..b54458e 100644 --- a/packages/client/src/apis/UserApi.ts +++ b/packages/client/src/apis/UserApi.ts @@ -18,7 +18,7 @@ import type { GenericError, RatingHistory, RegisterUserRequest, -} from '../models/index'; +} from '../models'; export interface ActivateUserOperationRequest { userId: string; diff --git a/packages/client/src/apis/index.ts b/packages/client/src/apis/index.ts index c27c6ff..6c795c5 100644 --- a/packages/client/src/apis/index.ts +++ b/packages/client/src/apis/index.ts @@ -10,5 +10,4 @@ export * from './MapApi'; export * from './MatchApi'; export * from './NotificationApi'; export * from './PvpGameApi'; -export * from './PvpLeaderboardApi'; export * from './UserApi'; diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index bebe8bb..be9d1ed 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -1,5 +1,5 @@ /* tslint:disable */ /* eslint-disable */ export * from './runtime'; -export * from './apis/index'; -export * from './models/index'; +export * from './apis'; +export * from './models'; diff --git a/packages/client/src/runtime.ts b/packages/client/src/runtime.ts index 2fe6a42..c8ef799 100644 --- a/packages/client/src/runtime.ts +++ b/packages/client/src/runtime.ts @@ -98,10 +98,6 @@ export const DefaultConfig = new Configuration(); * This is the base class for all generated API classes. */ export class BaseAPI { - private static readonly jsonRegex = new RegExp( - '^(:?application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(:?;.*)?$', - 'i', - ); private middleware: Middleware[]; constructor(protected configuration = DefaultConfig) { @@ -130,23 +126,6 @@ export class BaseAPI { return this.withMiddleware(...middlewares); } - /** - * Check if the given MIME is a JSON MIME. - * JSON MIME examples: - * application/json - * application/json; charset=UTF8 - * APPLICATION/JSON - * application/vnd.company+json - * @param mime - MIME (Multipurpose Internet Mail Extensions) - * @return True if the given MIME is JSON, false otherwise. - */ - protected isJsonMime(mime: string | null | undefined): boolean { - if (!mime) { - return false; - } - return BaseAPI.jsonRegex.test(mime); - } - protected async request( context: RequestOpts, initOverrides?: RequestInit | InitOverrideFunction, @@ -203,22 +182,14 @@ export class BaseAPI { })), }; - let body: any; - if ( - isFormData(overriddenInit.body) || - overriddenInit.body instanceof URLSearchParams || - isBlob(overriddenInit.body) - ) { - body = overriddenInit.body; - } else if (this.isJsonMime(headers['Content-Type'])) { - body = JSON.stringify(overriddenInit.body); - } else { - body = overriddenInit.body; - } - const init: RequestInit = { ...overriddenInit, - body, + body: + isFormData(overriddenInit.body) || + overriddenInit.body instanceof URLSearchParams || + isBlob(overriddenInit.body) + ? overriddenInit.body + : JSON.stringify(overriddenInit.body), }; return { url, init }; diff --git a/packages/renderer/src/events/EventEmitter.ts b/packages/renderer/src/events/EventEmitter.ts index 088b89b..995064f 100644 --- a/packages/renderer/src/events/EventEmitter.ts +++ b/packages/renderer/src/events/EventEmitter.ts @@ -30,6 +30,8 @@ class RendererEvents { static readonly SHUTDOWN = 'SHUTDOWN'; static readonly LOAD_LOG = 'LOAD_LOG'; + + static readonly SHOOT_PVP = 'SHOOT_PVP'; } const events = new Phaser.Events.EventEmitter(); diff --git a/packages/renderer/src/parser/Instructions.ts b/packages/renderer/src/parser/Instructions.ts index 366fe1f..1ab353c 100644 --- a/packages/renderer/src/parser/Instructions.ts +++ b/packages/renderer/src/parser/Instructions.ts @@ -50,7 +50,10 @@ export class Move { posY: number; - constructor(id: number, posX: number, posY: number) { + actorType: string; + + constructor(actorType: string, id: number, posX: number, posY: number) { + this.actorType = actorType; this.id = id; this.posX = posX; this.posY = posY; @@ -85,13 +88,17 @@ export class Spawn { hp: number; + actorType: string; + constructor( + actorType: string, id: number, typeId: number, posX: number, posY: number, hp: number, ) { + this.actorType = actorType; this.id = id; this.typeId = typeId; this.posX = posX; diff --git a/packages/renderer/src/parser/Parser.ts b/packages/renderer/src/parser/Parser.ts index e2a959d..03581f8 100644 --- a/packages/renderer/src/parser/Parser.ts +++ b/packages/renderer/src/parser/Parser.ts @@ -35,9 +35,10 @@ export function Parse(line: string): instructions.Instruction { if (ParsedArray[0] === 'MOVE') { return new instructions.Move( - Number(ParsedArray[1]), + String(ParsedArray[1]), Number(ParsedArray[2]), Number(ParsedArray[3]), + Number(ParsedArray[4]), ); } @@ -52,11 +53,12 @@ export function Parse(line: string): instructions.Instruction { if (ParsedArray[0] === 'SPAWN') { return new instructions.Spawn( - Number(ParsedArray[1]), + String(ParsedArray[1]), Number(ParsedArray[2]), Number(ParsedArray[3]), Number(ParsedArray[4]), Number(ParsedArray[5]), + Number(ParsedArray[6]), ); } diff --git a/packages/renderer/src/scenes/TileMap.ts b/packages/renderer/src/scenes/TileMap.ts index 46ab612..c1d1eb6 100644 --- a/packages/renderer/src/scenes/TileMap.ts +++ b/packages/renderer/src/scenes/TileMap.ts @@ -14,9 +14,11 @@ import { Parse } from '../parser/Parser.js'; export class TileMap extends Phaser.Scene { controls!: Phaser.Cameras.Controls.SmoothedKeyControl; - troops: Troop[] = []; + troops: Map = new Map(); - towers: Tower[] = []; + opponent_troops: Map = new Map(); + + towers: Map = new Map(); spriteGroup!: Phaser.GameObjects.Group; @@ -157,13 +159,13 @@ export class TileMap extends Phaser.Scene { ); tower.setDepth(tower.y); healthBar.setDepth(tower.depth); - this.towers.push(this.add.existing(tower)); + this.towers.set(this.towers.size, this.add.existing(tower)); }, ); events.on( RendererEvents.SPAWN_TROOP, - (typeId: number, x: number, y: number) => { + (typeId: number, id: number, x: number, y: number, actorType = 'A') => { const tile = this.groundLayer.getTileAt(x, y); let direction: Direction = 'northEast'; if (y === 0) { @@ -196,28 +198,36 @@ export class TileMap extends Phaser.Scene { direction, ); troop.setDepth(troop.y); - this.troops.push(this.add.existing(troop)); + actorType === 'D' + ? this.opponent_troops.set(id, troop) + : this.troops.set(id, this.add.existing(troop)); }, ); - events.on(RendererEvents.MOVE_TROOP, (id: number, x: number, y: number) => { - const troop = this.troops[id]; - const tile = this.groundLayer.getTileAt(x, y); - troop.moveTo( - tile.pixelX + Parameters.mapTileHalfWidth, - tile.pixelY + Parameters.mapTileHalfHeight, - ); - }); + events.on( + RendererEvents.MOVE_TROOP, + (id: number, x: number, y: number, actorType = 'A') => { + const troop = + actorType === 'D' + ? this.opponent_troops.get(id) + : this.troops.get(id); + const tile = this.groundLayer.getTileAt(x, y); + troop?.moveTo( + tile.pixelX + Parameters.mapTileHalfWidth, + tile.pixelY + Parameters.mapTileHalfHeight, + ); + }, + ); events.on( RendererEvents.SHOOT_TROOP, (towerId: number, troopId: number, newTroopHp: number) => { - const troop = this.troops[troopId]; - const tower = this.towers[towerId]; + const troop = this.troops.get(troopId); + const tower = this.towers.get(towerId); + if (troop === undefined || tower === undefined) return; troop.setHp(newTroopHp); - - const dx = troop.x - tower.x; - const dy = troop.y - tower.y; + const dx = troop?.x - tower?.x; + const dy = troop?.y - tower?.y; const angle = Phaser.Math.RadToDeg(Math.atan2(dy, dx / 2)); const attackArc = this.add.arc( @@ -245,13 +255,16 @@ export class TileMap extends Phaser.Scene { events.on( RendererEvents.SHOOT_TOWER, (troopId: number, towerId: number, newTowerHp: number) => { - const troop = this.troops[troopId]; - const tower = this.towers[towerId]; - const dx = tower.x - troop.x; - const dy = tower.y - troop.y; + const attacker = this.troops.get(troopId); + const tower = this.towers.get(towerId); + if (attacker === undefined || tower === undefined) { + return; + } + const dx = tower.x - attacker.x; + const dy = tower.y - attacker.y; const angle = Phaser.Math.RadToDeg(Math.atan2(dy, dx / 2)); - if (troop.troopType.spritesheet !== 'drone') { - const laserSprite = this.add.image(troop.x, troop.y, 'laser'); + if (attacker.troopType.spritesheet !== 'drone') { + const laserSprite = this.add.image(attacker.x, attacker.y, 'laser'); laserSprite.setRotation(angle); this.tweens.add({ targets: [laserSprite], @@ -262,7 +275,7 @@ export class TileMap extends Phaser.Scene { onComplete: () => laserSprite.destroy(), }); } else { - const bombSprite = this.add.sprite(troop.x, troop.y, 'bomb', 0); + const bombSprite = this.add.sprite(attacker.x, attacker.y, 'bomb', 0); bombSprite.setRotation(angle); this.anims.create({ key: 'bomb', @@ -284,13 +297,75 @@ export class TileMap extends Phaser.Scene { }); } tower.setHp(newTowerHp); - troop.attack(tower.x, tower.y); + attacker.attack(tower.x, tower.y); + }, + ); + + events.on( + RendererEvents.SHOOT_PVP, + ( + shooterId: number, + targetId: number, + newTargetHp: number, + isopponent: boolean, + ) => { + const shooter = isopponent + ? this.opponent_troops.get(shooterId) + : this.troops.get(shooterId); + const target = isopponent + ? this.towers.get(targetId) + : this.opponent_troops.get(targetId); + if (shooter === undefined || target === undefined) { + return; + } + const dx = target.x - shooter.x; + const dy = target.y - shooter.y; + const angle = Phaser.Math.RadToDeg(Math.atan2(dy, dx / 2)); + if (shooter.troopType.spritesheet !== 'drone') { + const laserSprite = this.add.image(shooter.x, shooter.y, 'laser'); + laserSprite.setRotation(angle); + this.tweens.add({ + targets: [laserSprite], + x: target.x, + y: target.y, + duration: Parameters.timePerTurn, + ease: 'Power2', + onComplete: () => laserSprite.destroy(), + }); + } else { + const bombSprite = this.add.sprite(shooter.x, shooter.y, 'bomb', 0); + bombSprite.setRotation(angle); + this.anims.create({ + key: 'bomb', + frames: this.anims.generateFrameNumbers('bomb', { + start: 0, + end: 2, + }), + frameRate: 10, + repeat: 0, + }); + bombSprite.play('bomb'); + this.tweens.add({ + targets: [bombSprite], + x: target.x, + y: target.y, + duration: Parameters.timePerTurn, + ease: 'Power2', + onComplete: () => bombSprite.destroy(), + }); + } + target.setHp(newTargetHp); + shooter.attack(target.x, target.y); }, ); events.on(RendererEvents.DEAD, (actorType: string, id: number) => { - if (actorType === 'A') { - const troop = this.troops[id]; + if (actorType === 'A' || (actorType === 'D' && this.towers.size === 0)) { + const troop = + actorType === 'A' + ? this.troops.get(id) + : this.opponent_troops.get(id); + if (troop === undefined) return; troop.dead(); this.tweens.add({ targets: [troop, troop.healthBar.bar, troop.shadow?.shadow], @@ -303,7 +378,8 @@ export class TileMap extends Phaser.Scene { }, }); } else if (actorType === 'D') { - const tower = this.towers[id]; + const tower = this.towers.get(id); + if (tower === undefined) return; this.tweens.add({ targets: [tower, tower.healthBar], alpha: 0, @@ -362,8 +438,10 @@ export class TileMap extends Phaser.Scene { events.emit( RendererEvents.SPAWN_TROOP, instruction.typeId, + instruction.id, instruction.posX, instruction.posY, + instruction.actorType, ); } else if (instruction instanceof Instructions.Move) { events.emit( @@ -371,8 +449,28 @@ export class TileMap extends Phaser.Scene { instruction.id, instruction.posX, instruction.posY, + instruction.actorType, ); } else if (instruction instanceof Instructions.Shoot) { + if (this.towers.size === 0) { + if (instruction.target === 'A') { + events.emit( + RendererEvents.SHOOT_PVP, + instruction.id1, + instruction.id2, + instruction.targetNewHp, + true, + ); + } else if (instruction.target === 'D') { + events.emit( + RendererEvents.SHOOT_PVP, + instruction.id1, + instruction.id2, + instruction.targetNewHp, + false, + ); + } + } if (instruction.target === 'A') { events.emit( RendererEvents.SHOOT_TROOP, @@ -456,8 +554,8 @@ export class TileMap extends Phaser.Scene { tower.healthBar.destroy(); tower.destroy(); }); - this.troops = []; - this.towers = []; + this.troops = new Map(); + this.towers = new Map(); const tweens = this.tweens.getAllTweens(); tweens.forEach(tween => { diff --git a/src/components/CommitModal/CommitModal.tsx b/src/components/CommitModal/CommitModal.tsx index 239c093..001ce91 100644 --- a/src/components/CommitModal/CommitModal.tsx +++ b/src/components/CommitModal/CommitModal.tsx @@ -2,8 +2,8 @@ import { CodeApi, Language } from '@codecharacter-2024/client'; import React, { useState } from 'react'; import { Button, Form, Modal, Row } from 'react-bootstrap'; import { apiConfig, ApiError } from '../../api/ApiConfig'; -import { UserCode, UserLanguage } from '../../store/editor/code'; - +import { GameType, UserCode, UserLanguage } from '../../store/editor/code'; +import { CurrentGameType } from '../../store/editor/code'; import { IsCommitModalOpen, isCommitModalOpened, @@ -17,7 +17,7 @@ const CommitModal = (): JSX.Element => { const isCommitModalOpen = useAppSelector(IsCommitModalOpen); const userLanguage = useAppSelector(UserLanguage); const userCode = useAppSelector(UserCode); - + const currentCodeState = useAppSelector(CurrentGameType); const dispatch = useAppDispatch(); const codeAPI = new CodeApi(apiConfig); @@ -39,6 +39,7 @@ const CommitModal = (): JSX.Element => { code: userCode, message: commitName, language: languageType, + codeType: currentCodeState === GameType.NORMAL ? 'NORMAL' : 'PVP', }) .then(() => { Toast.success('Code Committed'); diff --git a/src/components/Editor/Editor.tsx b/src/components/Editor/Editor.tsx index f9dea07..b696630 100644 --- a/src/components/Editor/Editor.tsx +++ b/src/components/Editor/Editor.tsx @@ -32,8 +32,6 @@ import { UserCode, } from '../../store/editor/code'; -import { updatePvPUserCode, PvPUserCode } from '../../store/PvP/pvpCode'; - import { codeCommitIDChanged, codeCommitNameChanged, @@ -172,8 +170,6 @@ export default function CodeEditor(props: Editor.Props): JSX.Element { dispatch(updateUserCode(codeNlanguage)); } else if (props.page == 'DailyChallenge') { dispatch(changeDcCode(codeNlanguage)); - } else { - dispatch(updatePvPUserCode(codeNlanguage)); } }); diff --git a/src/components/Leaderboard/Leaderboard.tsx b/src/components/Leaderboard/Leaderboard.tsx index 290c6c2..c5ed41d 100644 --- a/src/components/Leaderboard/Leaderboard.tsx +++ b/src/components/Leaderboard/Leaderboard.tsx @@ -10,7 +10,6 @@ import { LeaderboardEntry, MatchMode, TierType, - PvpLeaderboardApi, PvPLeaderBoardResponse, DailyChallengeLeaderBoardResponse, DailyChallengesApi, @@ -51,6 +50,8 @@ function PaginatedItems(props: LeaderboardType.Props) { const itemsPerPage = 8; const currentUserName = useAppSelector(user).username; + const leaderboardAPI = new LeaderboardApi(apiConfig); + switch (props.page) { case 'PvP': useEffect(() => { @@ -103,7 +104,6 @@ function PaginatedItems(props: LeaderboardType.Props) { const fetchLeaderboardByTier = (pageNum: number, tier?: TierType) => { setIsLoaded(false); - const leaderboardAPI = new LeaderboardApi(apiConfig); leaderboardAPI .getLeaderboard(pageNum, itemsPerPage, tier) .then(response => { @@ -126,8 +126,7 @@ function PaginatedItems(props: LeaderboardType.Props) { const fetchPvPLeaderboard = (pageNum: number) => { setIsPvPLoaded(false); - const pvpLeaderBoardApi = new PvpLeaderboardApi(apiConfig); - pvpLeaderBoardApi + leaderboardAPI .getPvPLeaderboard(pageNum, itemsPerPage) .then(response => { setPvpItems(response); @@ -136,7 +135,7 @@ function PaginatedItems(props: LeaderboardType.Props) { .catch(error => { if (error instanceof ApiError) Toast.error(error.message); }); - pvpLeaderBoardApi + leaderboardAPI .getPvPLeaderboard(pageNum + 1, itemsPerPage) .then(response => { setPvpNextItems(response); @@ -149,9 +148,8 @@ function PaginatedItems(props: LeaderboardType.Props) { const fetchDcLeaderboard = (pageNum: number) => { setIsDcLoaded(false); - const dcLeaderBoardApi = new DailyChallengesApi(apiConfig); - dcLeaderBoardApi - .getDailyChallengeLeaderBoard(pageNum, itemsPerPage) + const DcApi = new DailyChallengesApi(apiConfig); + DcApi.getDailyChallengeLeaderBoard(pageNum, itemsPerPage) .then(response => { setDcItems(response); setIsDcLoaded(true); @@ -159,8 +157,7 @@ function PaginatedItems(props: LeaderboardType.Props) { .catch(error => { if (error instanceof ApiError) Toast.error(error.message); }); - dcLeaderBoardApi - .getDailyChallengeLeaderBoard(pageNum + 1, itemsPerPage) + DcApi.getDailyChallengeLeaderBoard(pageNum + 1, itemsPerPage) .then(response => { setDcNextItems(response); setIsLoaded(true); diff --git a/src/components/PvPSelfMatchMakingModal/PvPSelfMatchMakeModal.tsx b/src/components/PvPSelfMatchMakingModal/PvPSelfMatchMakeModal.tsx index 9203dcd..a090a6e 100644 --- a/src/components/PvPSelfMatchMakingModal/PvPSelfMatchMakeModal.tsx +++ b/src/components/PvPSelfMatchMakingModal/PvPSelfMatchMakeModal.tsx @@ -20,6 +20,7 @@ import { AuthApi, CodeApi, CodeRevision, + CodeType, MatchApi, MatchMode, } from '@codecharacter-2024/client'; @@ -45,7 +46,7 @@ const PvPSelfMatchModal = (): JSX.Element => { if (localStorage.getItem('token') != null) { const codeApi = new CodeApi(apiConfig); codeApi - .getCodeRevisions() + .getCodeRevisions(CodeType.Pvp) .then(codeResp => setCodeHistory(codeResp)) .catch(error => { if (error instanceof ApiError) Toast.error(error.message); @@ -92,7 +93,7 @@ const PvPSelfMatchModal = (): JSX.Element => { // figure out how to create pvp match matchAPI .createMatch({ - mode: MatchMode.Pvp, + mode: MatchMode.Selfpvp, codeRevisionId: Code1CommitID, codeRevisionId2: Code2CommitID, }) diff --git a/src/components/Websocket/Websocket.tsx b/src/components/Websocket/Websocket.tsx index 2825092..8e3cde0 100644 --- a/src/components/Websocket/Websocket.tsx +++ b/src/components/Websocket/Websocket.tsx @@ -10,13 +10,15 @@ import { Stomp } from '@stomp/stompjs'; import { useEffect, useState } from 'react'; import Toast from 'react-hot-toast'; import { getLogAction } from '../../store/rendererLogs/logSlice'; -import { useAppDispatch } from '../../store/hooks'; +import { useAppDispatch, useAppSelector } from '../../store/hooks'; import { BASE_PATH } from '../../config/config'; +import { CurrentGameType } from '../../store/editor/code'; import { changeSimulationState } from '../../store/DailyChallenge/dailyChallenge'; export const Websocket: React.FunctionComponent = () => { const currentUserapi = new CurrentUserApi(apiConfig); const dispatch = useAppDispatch(); + const gameType = useAppSelector(CurrentGameType); const [user, setUser] = useState(); useEffect(() => { @@ -40,6 +42,7 @@ export const Websocket: React.FunctionComponent = () => { getLogAction({ id: game.id, callback: () => (window.location.href = './#/dashboard'), + gameType: gameType, }), ); break; @@ -50,6 +53,7 @@ export const Websocket: React.FunctionComponent = () => { getLogAction({ id: game.id, callback: () => (window.location.href = './#/dashboard'), + gameType: gameType, }), ); break; diff --git a/src/pages/Dashboard/Dashboard.tsx b/src/pages/Dashboard/Dashboard.tsx index cd1a127..58250cb 100644 --- a/src/pages/Dashboard/Dashboard.tsx +++ b/src/pages/Dashboard/Dashboard.tsx @@ -30,6 +30,10 @@ import { initializeEditorStates, UserCode, UserLanguage, + initializePvPEditorStates, + updateEditorCodeState, + GameType, + CurrentGameType, } from '../../store/editor/code'; import { useAppDispatch, useAppSelector } from '../../store/hooks'; import styles from './Dashboard.module.css'; @@ -70,11 +74,6 @@ import { dcSimulation, changePageState, } from '../../store/DailyChallenge/dailyChallenge'; -import { - changePvPLanguage, - PvPUserCode, - PvPUserLanguage, -} from '../../store/PvP/pvpCode'; import Tour from '../../components/TourProvider/TourProvider'; import { EditorSteps } from '../../components/TourProvider/EditorSteps'; import { useNavigate } from 'react-router-dom'; @@ -130,17 +129,15 @@ export default function Dashboard(): JSX.Element { const userCode = useAppSelector(UserCode); const dailyChallengeCode = useAppSelector(dcCode); - const pvpCode = useAppSelector(PvPUserCode); const dispatch = useAppDispatch(); const dailyChallenge = useAppSelector(dailyChallengeState); const pageState = useAppSelector(dailyChallengePageState); const dailyChallengeSimulationState = useAppSelector(dcSimulation); + const currentGameType = useAppSelector(CurrentGameType); const userLanguage = pageState == 'Dashboard' ? useAppSelector(UserLanguage) - : pageState == 'DailyChallenge' - ? useAppSelector(dcCodeLanguage) - : useAppSelector(PvPUserLanguage); + : useAppSelector(dcCodeLanguage); const codeAPI = new CodeApi(apiConfig); const dailyChallengeAPI = new DailyChallengesApi(apiConfig); @@ -179,6 +176,15 @@ export default function Dashboard(): JSX.Element { if (err instanceof ApiError) Toast.error(err.message); }) .finally(() => localStorage.setItem('firstTime', 'false')); + + codeAPI + .getLatestCode('PVP') + .then(response => { + dispatch(initializePvPEditorStates(response)); + }) + .catch(err => { + if (err instanceof ApiError) Toast.error(err.message); + }); } }, []); @@ -203,10 +209,6 @@ export default function Dashboard(): JSX.Element { case 'DailyChallenge': dispatch(changeDcLanguage('c_cpp')); break; - case 'PvP': - console.log('it came to pvp too'); - dispatch(changePvPLanguage('c_cpp')); - break; default: dispatch(changeLanguage('c_cpp')); } @@ -214,39 +216,31 @@ export default function Dashboard(): JSX.Element { localStorage.setItem('languageChose', 'C++'); break; case 'Python': - console.log('here'); pageState == 'Dashboard' ? dispatch(changeLanguage('python')) - : pageState == 'DailyChallenge' - ? dispatch(changeDcLanguage('python')) - : dispatch(changePvPLanguage('python')); + : dispatch(changeDcLanguage('python')); setLanguageChose('Python'); localStorage.setItem('languageChose', 'Python'); break; case 'Java': pageState == 'Dashboard' ? dispatch(changeLanguage('java')) - : pageState == 'DailyChallenge' - ? dispatch(changeDcLanguage('java')) - : dispatch(changePvPLanguage('java')); + : dispatch(changeDcLanguage('java')); + setLanguageChose('Java'); localStorage.setItem('languageChose', 'Java'); break; default: pageState == 'Dashboard' ? dispatch(changeLanguage('c_cpp')) - : pageState == 'DailyChallenge' - ? dispatch(changeDcLanguage('c_cpp')) - : dispatch(changePvPLanguage('c_cpp')); + : dispatch(changeDcLanguage('c_cpp')); } }; const handlePvPTake = () => { - dispatch(changePageState('PvP')); - navigate('/dashboard', { replace: true }); + dispatch(updateEditorCodeState(GameType.PVP)); }; const handlePvPClose = () => { - dispatch(changePageState('Dashboard')); - navigate('/dashboard'); + dispatch(updateEditorCodeState(GameType.NORMAL)); }; const handleSave = () => { @@ -259,16 +253,11 @@ export default function Dashboard(): JSX.Element { .updateLatestCode({ codeType: pageState == 'Dashboard' - ? 'NORMAL' - : pageState == 'DailyChallenge' - ? 'DAILY_CHALLENGE' - : 'PVP', - code: - pageState == 'Dashboard' - ? userCode - : pageState == 'DailyChallenge' - ? dailyChallengeCode - : pvpCode, + ? currentGameType == GameType.NORMAL + ? 'NORMAL' + : 'PVP' + : 'DAILY_CHALLENGE', + code: pageState == 'Dashboard' ? userCode : dailyChallengeCode, lock: false, language: languageType, }) @@ -281,20 +270,22 @@ export default function Dashboard(): JSX.Element { }; function handleSimulate() { - dispatch(isSelfMatchModalOpened(true)); - dispatch(codeCommitNameChanged('Current Code')); - dispatch(codeCommitIDChanged(null)); - dispatch(mapCommitNameChanged('Current Map')); - dispatch(mapCommitIDChanged(null)); + if (currentGameType == GameType.NORMAL) { + dispatch(isSelfMatchModalOpened(true)); + dispatch(codeCommitNameChanged('Current Code')); + dispatch(codeCommitIDChanged(null)); + dispatch(mapCommitNameChanged('Current Map')); + dispatch(mapCommitIDChanged(null)); + } else { + dispatch(isPvPSelfMatchModalOpened(true)); + dispatch(code1CommitNameChanged('Current Code')); + dispatch(code1CommitIDChanged(null)); + dispatch(code2CommitNameChanged('Current Code')); + dispatch(code2CommitIDChanged(null)); + } } - function handlePvPSimulate() { - dispatch(isPvPSelfMatchModalOpened(true)); - dispatch(code1CommitNameChanged('Current Player 1 Code')); - dispatch(code1CommitIDChanged(null)); - dispatch(code2CommitNameChanged('Current Player 2 Code')); - dispatch(code2CommitIDChanged(null)); - } + function handlePvPSimulate() {} const isCommitModalOpen = useAppSelector(IsCommitModalOpen); @@ -327,21 +318,16 @@ export default function Dashboard(): JSX.Element { .updateLatestCode({ codeType: pageState == 'Dashboard' - ? 'NORMAL' - : pageState == 'DailyChallenge' - ? 'DAILY_CHALLENGE' - : 'PVP', - code: - pageState == 'Dashboard' - ? userCode - : pageState == 'DailyChallenge' - ? dailyChallengeCode - : pvpCode, + ? currentGameType == GameType.NORMAL + ? 'NORMAL' + : 'PVP' + : 'DAILY_CHALLENGE', + code: pageState == 'Dashboard' ? userCode : dailyChallengeCode, lock: true, language: languageType, }) .then(() => { - if (pageState == 'Dashboard' || pageState == 'PvP') { + if (pageState == 'Dashboard') { Toast.success('Code Submitted'); } }) @@ -408,9 +394,7 @@ export default function Dashboard(): JSX.Element { minSize={520} >
- {pageState == 'Dashboard' || - pageState == 'PvP' || - dailyChallenge.challType == 'MAP' ? ( + {pageState == 'Dashboard' || dailyChallenge.challType == 'MAP' ? (
- - - - - - ) : ( <> )} @@ -540,7 +494,9 @@ export default function Dashboard(): JSX.Element { e.target.value == 'PvP' ? handlePvPTake() @@ -638,7 +594,6 @@ export default function Dashboard(): JSX.Element { )}
{pageState == 'Dashboard' || - pageState == 'PvP' || dailyChallenge.challType == 'MAP' ? (
- {pageState == 'Dashboard' || - pageState == 'PvP' || - dailyChallengeSimulationState ? ( + {pageState == 'Dashboard' || dailyChallengeSimulationState ? ( ) : dailyChallenge.challType == 'MAP' ? ( <> @@ -715,9 +664,7 @@ export default function Dashboard(): JSX.Element { )}
- {pageState == 'Dashboard' || - pageState == 'PvP' || - dailyChallengeSimulationState ? ( + {pageState == 'Dashboard' || dailyChallengeSimulationState ? ( ) : ( <> diff --git a/src/store/DailyChallenge/dailyChallenge.ts b/src/store/DailyChallenge/dailyChallenge.ts index 93cc68e..c839e46 100644 --- a/src/store/DailyChallenge/dailyChallenge.ts +++ b/src/store/DailyChallenge/dailyChallenge.ts @@ -11,7 +11,7 @@ import defaultJavaCode from '../../assets/codes/java/Run.java?raw'; export interface DailyChallengeStateType { dailyChallenge: DailyChallengeGetRequest; - pageType: 'Dashboard' | 'DailyChallenge' | 'PvP'; + pageType: 'Dashboard' | 'DailyChallenge'; dcCode: string; dcAllLanguagesCode: string[]; codeLanguage: string; @@ -64,7 +64,7 @@ export const dailyChallengeSlice = createSlice({ }, changePageState: ( state, - action: PayloadAction<'Dashboard' | 'DailyChallenge' | 'PvP'>, + action: PayloadAction<'Dashboard' | 'DailyChallenge'>, ) => { state.pageType = action.payload; }, @@ -110,7 +110,7 @@ export const dailyChallengeState = ( ): DailyChallengeGetRequest => state.dailyChallenge.dailyChallenge; export const dailyChallengePageState = ( state: RootState, -): 'Dashboard' | 'DailyChallenge' | 'PvP' => state.dailyChallenge.pageType; +): 'Dashboard' | 'DailyChallenge' => state.dailyChallenge.pageType; export const dailyChallengeCompletionState = ( state: RootState, ): boolean | undefined => state.dailyChallenge.dailyChallenge.completionStatus; diff --git a/src/store/PvP/pvpCode.ts b/src/store/PvP/pvpCode.ts deleted file mode 100644 index 8e9d084..0000000 --- a/src/store/PvP/pvpCode.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Code } from '@codecharacter-2024/client'; -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { RootState } from '../store'; -import defaultCppCode from '../../assets/codes/cpp/runpvp.cpp?raw'; -import defaultPythonCode from '../../assets/codes/python/runpvp.py?raw'; -import defaultJavaCode from '../../assets/codes/java/RunPvP.java?raw'; - -export const languagesAvailable = ['c_cpp', 'python', 'java']; - -export interface pvpEditorStateType { - pvpAllLanguagesCode: string[]; - pvpUserCode: string; - pvpLanguage: string; - pvpLastSavedAt: Date; -} - -const initialState: pvpEditorStateType = { - pvpAllLanguagesCode: [defaultCppCode, defaultPythonCode, defaultJavaCode], - pvpUserCode: '', - pvpLanguage: 'c_cpp', - pvpLastSavedAt: new Date(), -}; - -export interface PvPCodeAndLanguage { - currentUserCode: string; - currentUserLanguage: string; -} - -export const pvpEditorSlice = createSlice({ - name: 'pvpEditorState', - initialState: initialState, - reducers: { - initializePvPEditorStates: (state, action: PayloadAction) => { - state.pvpUserCode = action.payload.code; - if (action.payload.language === 'C' || action.payload.language === 'CPP') - state.pvpLanguage = 'c_cpp'; - else if (action.payload.language === 'PYTHON') - state.pvpLanguage = 'python'; - else if (action.payload.language === 'JAVA') state.pvpLanguage = 'java'; - state.pvpLastSavedAt = action.payload.lastSavedAt; - const desiredIndex = languagesAvailable.indexOf(state.pvpLanguage); - state.pvpAllLanguagesCode[desiredIndex] = action.payload.code; - }, - - updatePvPUserCode: (state, action: PayloadAction) => { - const tempCurrentUserLanguage = action.payload.currentUserLanguage; - const desiredIndex = languagesAvailable.indexOf(tempCurrentUserLanguage); - const newCodeAndLanguage: PvPCodeAndLanguage = { - currentUserCode: action.payload.currentUserCode, - currentUserLanguage: action.payload.currentUserLanguage, - }; - - state.pvpAllLanguagesCode[desiredIndex] = - newCodeAndLanguage.currentUserCode; - state.pvpUserCode = newCodeAndLanguage.currentUserCode; - }, - - changePvPLanguage: (state, action: PayloadAction) => { - console.log('lang changed'); - const tempCurrentUserLanguage = action.payload; - const desiredIndex = languagesAvailable.indexOf(tempCurrentUserLanguage); - state.pvpUserCode = state.pvpAllLanguagesCode[desiredIndex]; - state.pvpLanguage = action.payload; - }, - }, -}); - -export const { - updatePvPUserCode, - changePvPLanguage, - initializePvPEditorStates, -} = pvpEditorSlice.actions; -export const PvPUserCode = (state: RootState): string => - state.pvpEditorReducer.pvpUserCode; -export const PvPUserLanguage = (state: RootState): string => - state.pvpEditorReducer.pvpLanguage; -export default pvpEditorSlice.reducer; diff --git a/src/store/editor/code.ts b/src/store/editor/code.ts index 9e4162a..7c93b92 100644 --- a/src/store/editor/code.ts +++ b/src/store/editor/code.ts @@ -4,21 +4,37 @@ import { RootState } from '../store'; import defaultCppCode from '../../assets/codes/cpp/run.cpp?raw'; import defaultPythonCode from '../../assets/codes/python/run.py?raw'; import defaultJavaCode from '../../assets/codes/java/Run.java?raw'; +import defaultPvPCppCode from '../../assets/codes/cpp/runpvp.cpp?raw'; +import defaultPvPPythonCode from '../../assets/codes/python/runpvp.py?raw'; +import defaultPvPJavaCode from '../../assets/codes/java/RunPvP.java?raw'; export const languagesAvailable = ['c_cpp', 'python', 'java']; +export enum GameType { + NORMAL = 'NORMAL', + PVP = 'PVP', +} + export interface editorStateType { - allLanguagesCode: string[]; + allLanguagesNormalCode: string[]; + allLanguagesPvPCode: string[]; userCode: string; language: string; lastSavedAt: Date; + gameType: GameType; } const initialState: editorStateType = { - allLanguagesCode: [defaultCppCode, defaultPythonCode, defaultJavaCode], + allLanguagesNormalCode: [defaultCppCode, defaultPythonCode, defaultJavaCode], + allLanguagesPvPCode: [ + defaultPvPCppCode, + defaultPvPPythonCode, + defaultPvPJavaCode, + ], userCode: '', language: 'c_cpp', lastSavedAt: new Date(), + gameType: GameType.NORMAL, }; export interface CodeAndLanguage { @@ -38,7 +54,18 @@ export const editorSlice = createSlice({ else if (action.payload.language === 'JAVA') state.language = 'java'; state.lastSavedAt = action.payload.lastSavedAt; const desiredIndex = languagesAvailable.indexOf(state.language); - state.allLanguagesCode[desiredIndex] = action.payload.code; + state.allLanguagesNormalCode[desiredIndex] = action.payload.code; + state.gameType = GameType.NORMAL; + }, + + initializePvPEditorStates: (state, action: PayloadAction) => { + if (action.payload.language === 'C' || action.payload.language === 'CPP') + state.language = 'c_cpp'; + else if (action.payload.language === 'PYTHON') state.language = 'python'; + else if (action.payload.language === 'JAVA') state.language = 'java'; + state.lastSavedAt = action.payload.lastSavedAt; + const desiredIndex = languagesAvailable.indexOf(state.language); + state.allLanguagesPvPCode[desiredIndex] = action.payload.code; }, updateUserCode: (state, action: PayloadAction) => { @@ -48,24 +75,54 @@ export const editorSlice = createSlice({ currentUserCode: action.payload.currentUserCode, currentUserLanguage: action.payload.currentUserLanguage, }; - - state.allLanguagesCode[desiredIndex] = newCodeAndLanguage.currentUserCode; + if (state.gameType === GameType.NORMAL) { + state.allLanguagesNormalCode[desiredIndex] = + newCodeAndLanguage.currentUserCode; + } else { + state.allLanguagesPvPCode[desiredIndex] = + newCodeAndLanguage.currentUserCode; + } state.userCode = newCodeAndLanguage.currentUserCode; }, + updateEditorCodeState: (state, action: PayloadAction) => { + state.gameType = action.payload; + console.log(state); + if (action.payload === GameType.NORMAL) { + state.userCode = + state.allLanguagesNormalCode[ + languagesAvailable.indexOf(state.language) + ]; + } else { + state.userCode = + state.allLanguagesPvPCode[languagesAvailable.indexOf(state.language)]; + } + }, + changeLanguage: (state, action: PayloadAction) => { const tempCurrentUserLanguage = action.payload; const desiredIndex = languagesAvailable.indexOf(tempCurrentUserLanguage); - state.userCode = state.allLanguagesCode[desiredIndex]; + if (state.gameType === GameType.NORMAL) { + state.userCode = state.allLanguagesNormalCode[desiredIndex]; + } else { + state.userCode = state.allLanguagesPvPCode[desiredIndex]; + } state.language = action.payload; }, }, }); -export const { updateUserCode, changeLanguage, initializeEditorStates } = - editorSlice.actions; +export const { + updateUserCode, + changeLanguage, + initializeEditorStates, + initializePvPEditorStates, + updateEditorCodeState, +} = editorSlice.actions; export const UserCode = (state: RootState): string => state.codeEditorReducer.editorState.userCode; export const UserLanguage = (state: RootState): string => state.codeEditorReducer.editorState.language; +export const CurrentGameType = (state: RootState): GameType => + state.codeEditorReducer.editorState.gameType; export default editorSlice.reducer; diff --git a/src/store/rendererLogs/logAPI.ts b/src/store/rendererLogs/logAPI.ts index cf8fdf7..79d01a7 100644 --- a/src/store/rendererLogs/logAPI.ts +++ b/src/store/rendererLogs/logAPI.ts @@ -1,18 +1,34 @@ import { ApiError, apiConfig } from '../../api/ApiConfig'; -import { GameApi } from '@codecharacter-2024/client'; +import { GameApi, PvpGameApi } from '@codecharacter-2024/client'; +import { GameType } from '../editor/code'; -export const getLogs = (id: string): Promise => { +export const getLogs = (id: string, gametype: GameType): Promise => { return new Promise((resolve, reject) => { const gameAPI = new GameApi(apiConfig); - gameAPI - .getGameLogsByGameId(id) - .then(logs => { - resolve(logs); - }) - .catch(error => { - if (error instanceof ApiError) { - reject(); - } - }); + const pvpGameApi = new PvpGameApi(apiConfig); + if (gametype === GameType.NORMAL) { + gameAPI + .getGameLogsByGameId(id) + .then(logs => { + console.log(logs); + resolve(logs); + }) + .catch(error => { + if (error instanceof ApiError) { + reject(); + } + }); + } else { + pvpGameApi + .getPvpGameLogsByGameId(id) + .then(logs => { + resolve(logs); + }) + .catch(error => { + if (error instanceof ApiError) { + reject(); + } + }); + } }); }; diff --git a/src/store/rendererLogs/logSlice.ts b/src/store/rendererLogs/logSlice.ts index 47e3278..31c3316 100644 --- a/src/store/rendererLogs/logSlice.ts +++ b/src/store/rendererLogs/logSlice.ts @@ -2,6 +2,7 @@ import { RendererUtils } from '@codecharacter-2024/renderer'; import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; import { RootState } from '../store'; import { getLogs } from './logAPI'; +import { GameType } from '../editor/code'; const initialState = { logs: '', @@ -9,9 +10,13 @@ const initialState = { export const getLogAction = createAsyncThunk( 'logs/getLogs', - async (idWithCallback: { id: string; callback: () => void }) => { + async (idWithCallback: { + id: string; + callback: () => void; + gameType: GameType; + }) => { try { - getLogs(idWithCallback.id).then(logs => { + getLogs(idWithCallback.id, idWithCallback.gameType).then(logs => { idWithCallback.callback(); setTimeout(() => { RendererUtils.loadLog(logs); diff --git a/src/store/store.ts b/src/store/store.ts index b00bb4c..44a51f0 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -14,7 +14,6 @@ import logReducer from './rendererLogs/logSlice'; import selfMatchModalReducer from './SelfMatchMakeModal/SelfMatchModal'; import PvPSelfMatchModalReducer from './PvPSelfMatchMakeModal/PvPSelfMatchModal'; import dailyChallengeReducer from './DailyChallenge/dailyChallenge'; -import pvpCodeReducer from './PvP/pvpCode'; const reducers = combineReducers({ editorState: editorReducer, @@ -39,7 +38,6 @@ export const store = configureStore({ selfMatchModal: selfMatchModalReducer, pvpSelfMatchModal: PvPSelfMatchModalReducer, dailyChallenge: dailyChallengeReducer, - pvpEditorReducer: pvpCodeReducer, }, middleware: [thunk], }); From 1001c9f4c764c05b31d7486b89d6ee32c6269a7d Mon Sep 17 00:00:00 2001 From: shubham-1806 Date: Tue, 6 Feb 2024 22:09:11 +0530 Subject: [PATCH 3/8] feat:pvp --- packages/renderer/src/scenes/TileMap.ts | 12 +- src/components/BattleTV/BattleTV.module.css | 52 +++ src/components/BattleTV/BattleTV.tsx | 336 +++++++++++++------- src/components/Editor/Editor.tsx | 14 +- src/components/Editor/EditorTypes.ts | 2 + src/components/Leaderboard/Leaderboard.tsx | 5 +- src/components/Websocket/Websocket.tsx | 4 +- src/pages/Dashboard/Dashboard.tsx | 12 +- src/store/BattleTV/BattleTvSlice.ts | 169 +++++++--- src/store/editor/code.ts | 26 +- src/store/rendererLogs/logAPI.ts | 4 +- 11 files changed, 447 insertions(+), 189 deletions(-) diff --git a/packages/renderer/src/scenes/TileMap.ts b/packages/renderer/src/scenes/TileMap.ts index c1d1eb6..d97f9ce 100644 --- a/packages/renderer/src/scenes/TileMap.ts +++ b/packages/renderer/src/scenes/TileMap.ts @@ -199,7 +199,7 @@ export class TileMap extends Phaser.Scene { ); troop.setDepth(troop.y); actorType === 'D' - ? this.opponent_troops.set(id, troop) + ? this.opponent_troops.set(id, this.add.existing(troop)) : this.troops.set(id, this.add.existing(troop)); }, ); @@ -554,9 +554,14 @@ export class TileMap extends Phaser.Scene { tower.healthBar.destroy(); tower.destroy(); }); + this.opponent_troops.forEach(troop => { + troop.removeAllListeners(); + troop.healthBar.destroy(); + troop.destroy(); + }); this.troops = new Map(); this.towers = new Map(); - + this.opponent_troops = new Map(); const tweens = this.tweens.getAllTweens(); tweens.forEach(tween => { tween.complete(); @@ -567,5 +572,8 @@ export class TileMap extends Phaser.Scene { this.troops.forEach(skeleton => { skeleton.update(); }); + this.opponent_troops.forEach(skeleton => { + skeleton.update(); + }); } } diff --git a/src/components/BattleTV/BattleTV.module.css b/src/components/BattleTV/BattleTV.module.css index 3737720..9d3182c 100644 --- a/src/components/BattleTV/BattleTV.module.css +++ b/src/components/BattleTV/BattleTV.module.css @@ -279,3 +279,55 @@ color: #0a1c2a; background-color: #aaaaaa; } + + +.whiteButton { + align-content: right; + color: #ffffff !important; + font-weight: 400; + font-family: Bai Jamjuree; + font-style: normal; + background-color: inherit !important; + font-size: 2em; + border-bottom: 2px solid white; + border-top: 0; + border-left: 0; + border-right: 0; + transition: 0.5s; + border-radius: 0; + padding: 0 50px; +} + +.darkButton { + align-content: right; + color: rgba(234, 234, 234, 0.6); + background-color: inherit !important; + font-family: Bai Jamjuree; + font-weight: 400; + font-style: normal; + border-bottom: 2px solid rgba(234, 234, 234, 0.6); + border-top: 0; + border-left: 0; + border-right: 0; + font-size: 2em; + transition: 0.5s; + border-radius: 0px; + box-shadow: none !important; + padding: 0 50px; +} + +.darkButton:hover { + color: #ffffff; +} + +.whiteButton:active, +.darkButton:active { + box-shadow: none; +} + +.buttonContainer { + display: flex; + flex-direction: row; + justify-content: center; + margin: 1rem; +} \ No newline at end of file diff --git a/src/components/BattleTV/BattleTV.tsx b/src/components/BattleTV/BattleTV.tsx index 825b9c4..ee3f3ea 100644 --- a/src/components/BattleTV/BattleTV.tsx +++ b/src/components/BattleTV/BattleTV.tsx @@ -4,15 +4,23 @@ import { getLogAction } from '../../store/rendererLogs/logSlice'; import { useNavigate } from 'react-router-dom'; import styles from './BattleTV.module.css'; import { - battleTvSelector, - fetchBattleTv, + normalBattleTvSelector, + pvpBattleTvSelector, + dcBattleTvSelector, + fetchBattlesAction, + BattleType, + loadingSelector, + hasErrorsSelector, } from '../../store/BattleTV/BattleTvSlice'; import { useAppSelector, useAppDispatch } from '../../store/hooks'; import { getAvatarByID } from '../Avatar/Avatar'; import { CurrentUserApi, + Game, Match, MatchMode, + PvPGame, + PvPMatch, Verdict, } from '@codecharacter-2024/client'; import { User, user } from '../../store/User/UserSlice'; @@ -24,8 +32,15 @@ import watchIcon from '../../assets/watch.png'; import { useTour } from '@reactour/tour'; import { apiConfig } from '../../api/ApiConfig'; import codecharacterIcon from '../../../public/assets/codechar_favicon.png'; +import { Button, ButtonGroup } from 'react-bootstrap'; +import { GameType } from '../../store/editor/code'; -function getIcon(loggedInUser: User, match: Match) { +interface MatchType { + Match: Match; + PvPMatch: PvPMatch; +} + +function getIcon(loggedInUser: User, match: MatchType[keyof MatchType]) { if (loggedInUser.username === match.user1.username) { // user is PLAYER1 if (match.matchVerdict === Verdict.Player1) { @@ -48,7 +63,7 @@ function getIcon(loggedInUser: User, match: Match) { return styles.battlecardtie; } -function getMatchMode(match: Match) { +function getMatchMode(match: Match | PvPMatch) { let style = ''; if (match.matchMode == MatchMode.Auto) { style = styles.automatch; @@ -56,44 +71,52 @@ function getMatchMode(match: Match) { return style; } -function getUsersGame(loggedInUser: User, match: Match) { - const games = [...match.games.values()]; - if (loggedInUser.username == match.user1.username) { - return games[0]; +function getUsersGame( + loggedInUser: User, + match: MatchType[keyof MatchType], +): Game | PvPGame { + if ('games' in match) { + const games = [...match.games.values()]; + if (loggedInUser.username == match.user1.username) { + return games[0]; + } else { + return games[games.length - 1]; + } } else { - return games[games.length - 1]; + return match.game; } } -function PaginatedItems() { +function PaginatedItems({ battleTvType }: { battleTvType: BattleType }) { const [pageCount, setPageCount] = useState(0); - const [itemOffset, setItemOffset] = useState(0); - const [currentItems, setCurrentItems] = useState([]); const navigate = useNavigate(); - - const itemsPerPage = 8; - - const { battletv, loading, hasbeenFetched, hasErrors } = - useAppSelector(battleTvSelector); - + const currentBattles = + battleTvType == BattleType.PVP + ? useAppSelector(pvpBattleTvSelector) + : battleTvType == BattleType.NORMAL + ? useAppSelector(normalBattleTvSelector) + : useAppSelector(dcBattleTvSelector); + const loading = useAppSelector(loadingSelector); const loggedInUser = useAppSelector(user); + const hasErrors = useAppSelector(hasErrorsSelector); // initialize the redux hook const dispatch = useAppDispatch(); - if (!hasbeenFetched) { - dispatch(fetchBattleTv()); + if (!currentBattles.hasbeenFetched) { + if (currentBattles.page !== pageCount) { + dispatch( + fetchBattlesAction({ battleTvType: battleTvType, page: pageCount }), + ); + } } - useEffect(() => { - const endOffset = itemOffset + itemsPerPage; - setCurrentItems(battletv.slice(itemOffset, endOffset)); - setPageCount(Math.ceil(battletv.length / itemsPerPage)); - }, [itemOffset, itemsPerPage, battletv]); - const handlePageClick = (event: { selected: number }) => { - const newOffset = (event.selected * itemsPerPage) % battletv.length; - setItemOffset(newOffset); + setPageCount(event.selected); + dispatch( + fetchBattlesAction({ battleTvType: battleTvType, page: event.selected }), + ); + console.log('changed'); }; return ( @@ -107,92 +130,108 @@ function PaginatedItems() {
) : ( <> - {currentItems.length == 0 ? ( + {currentBattles.battles.length == 0 ? (
You have not played any matches yet
) : ( - currentItems && - currentItems.map((match: Match) => ( -
-
- -
- -
- - {match.user1.username.substring(0, 10)} - -
- - {[...match.games.values()][0].coinsUsed} - - - {[...match.games.values()][0].destruction.toFixed(2)} - + currentBattles.battles && + currentBattles.battles.map( + (match: MatchType[keyof MatchType]) => ( +
{ - dispatch( - getLogAction({ - id: getUsersGame(loggedInUser, match).id, - callback: () => { - if (match.user2 === null) { - dispatch(changePageState('DailyChallenge')); - dispatch(changeSimulationState(true)); - } - navigate('/dashboard'); - }, - }), - ); - }} - > - -
- - {[...match.games.values()][ - [...match.games.values()].length === 1 ? 0 : 1 - ].destruction.toFixed(2)} - - - { - [...match.games.values()][ - [...match.games.values()].length === 1 ? 0 : 1 - ].coinsUsed + className={ + styles.item + + ' ' + + getIcon(loggedInUser, match) + + ' ' + + getMatchMode(match) } - - - - {match.user2 !== null - ? match.user2?.username - : 'Daily Challenge'} + > + +
+ +
+ + {match.user1.username.substring(0, 10)} +
-
- + + {'game' in match + ? match.game.scorePlayer1 + : [...match.games.values()][0].coinsUsed} + + + {'game' in match + ? '----' + : [...match.games.values()][0].destruction.toFixed(2)} + +
{ + dispatch( + getLogAction({ + //needs to be changed + id: + battleTvType === BattleType.PVP + ? match.id + : getUsersGame(loggedInUser, match).id, + callback: () => { + if (match.user2 === null) { + dispatch(changePageState('DailyChallenge')); + dispatch(changeSimulationState(true)); + } + navigate('/dashboard'); + }, + gameType: + battleTvType == BattleType.PVP + ? GameType.PVP + : GameType.NORMAL, + }), + ); + }} + > +
- + + {'game' in match + ? match.game.scorePlayer2 + : [...match.games.values()][ + [...match.games.values()].length === 1 ? 0 : 1 + ].destruction.toFixed(2)} + + + {'game' in match + ? '----' + : [...match.games.values()][ + [...match.games.values()].length === 1 ? 0 : 1 + ].coinsUsed} + + + + {match.user2 !== null + ? match.user2?.username + : 'Daily Challenge'} + +
+ +
+
+
-
- )) + ), + ) )} )} @@ -206,7 +245,7 @@ function PaginatedItems() { nextLinkClassName={styles.pageNum} breakLabel="..." breakLinkClassName={styles.pageNum} - pageCount={pageCount} + pageCount={69} marginPagesDisplayed={2} pageRangeDisplayed={5} onPageChange={handlePageClick} @@ -217,7 +256,9 @@ function PaginatedItems() { type="button" className={styles.button} onClick={() => { - dispatch(fetchBattleTv()); + dispatch( + fetchBattlesAction({ battleTvType: battleTvType, page: 0 }), + ); }} id="refresh" > @@ -230,6 +271,9 @@ function PaginatedItems() { export default function BattleTV(): JSX.Element { const { setIsOpen } = useTour(); const currentUserapi = new CurrentUserApi(apiConfig); + const [battleTvType, setBattleTvType] = useState( + BattleType.NORMAL, + ); useEffect(() => { setTimeout(() => { @@ -246,22 +290,72 @@ export default function BattleTV(): JSX.Element { return (
-
-

- Battle TV -

+
+
+ + + + + +
-
- ATTACKER - COINS USED - DESTRUCTION(%) - - DESTRUCTION(%) - COINS USED - DEFENDER -
- + {battleTvType !== BattleType.PVP ? ( +
+ ATTACKER + COINS USED + DESTRUCTION(%) + + DESTRUCTION(%) + COINS USED + DEFENDER +
+ ) : ( +
+ PLAYER 1 + SCORE + + SCORE + PLAYER 2 +
+ )} +
); diff --git a/src/components/Editor/Editor.tsx b/src/components/Editor/Editor.tsx index b696630..27403f8 100644 --- a/src/components/Editor/Editor.tsx +++ b/src/components/Editor/Editor.tsx @@ -68,9 +68,7 @@ export default function CodeEditor(props: Editor.Props): JSX.Element { const userCode: string = props.page == 'Dashboard' ? useAppSelector(UserCode) - : props.page == 'DailyChallenge' - ? useAppSelector(dcCode) - : useAppSelector(PvPUserCode); + : useAppSelector(dcCode); const fontSize: number = useAppSelector(FontSize); const theme: string = useAppSelector(Theme); const autocomplete: boolean = useAppSelector(Autocomplete); @@ -291,7 +289,15 @@ export default function CodeEditor(props: Editor.Props): JSX.Element { editor?.dispose(); wsClient?.close(1000); }; - }, [fontSize, theme, language, keyboardHandler, props.page, autocomplete]); + }, [ + fontSize, + theme, + language, + keyboardHandler, + props.page, + autocomplete, + props.gameType, + ]); return
; } diff --git a/src/components/Editor/EditorTypes.ts b/src/components/Editor/EditorTypes.ts index 6cc9b39..84a659f 100644 --- a/src/components/Editor/EditorTypes.ts +++ b/src/components/Editor/EditorTypes.ts @@ -1,4 +1,5 @@ import { RefObject } from 'react'; +import { GameType } from '../../store/editor/code'; interface PageType { Dashboard: 'Dashboard'; @@ -11,6 +12,7 @@ export type Props = { page: PageType[keyof PageType]; SaveRef: RefObject; SubmitRef: RefObject; + gameType: GameType; }; export type Workspace = { diff --git a/src/components/Leaderboard/Leaderboard.tsx b/src/components/Leaderboard/Leaderboard.tsx index c5ed41d..cc8380c 100644 --- a/src/components/Leaderboard/Leaderboard.tsx +++ b/src/components/Leaderboard/Leaderboard.tsx @@ -129,6 +129,7 @@ function PaginatedItems(props: LeaderboardType.Props) { leaderboardAPI .getPvPLeaderboard(pageNum, itemsPerPage) .then(response => { + console.log(response); setPvpItems(response); setIsPvPLoaded(true); }) @@ -350,8 +351,8 @@ function PaginatedItems(props: LeaderboardType.Props) {
- {items && - items.map((row: PvPLeaderBoardResponse) => ( + {pvpItems && + pvpItems.map((row: PvPLeaderBoardResponse) => ( { const currentUserapi = new CurrentUserApi(apiConfig); const dispatch = useAppDispatch(); const gameType = useAppSelector(CurrentGameType); + console.log(gameType.toString()); const [user, setUser] = useState(); useEffect(() => { @@ -36,6 +37,7 @@ export const Websocket: React.FunctionComponent = () => { break; case GameStatus.Executed: Toast.success('Executed successfully!'); + console.log(gameType); // TODO: find non-hacky way to do this dispatch(changeSimulationState(true)); dispatch( @@ -78,7 +80,7 @@ export const Websocket: React.FunctionComponent = () => { return () => { wsClient.deactivate(); }; - }, [user]); + }, [user, gameType]); useEffect(() => { if (localStorage.getItem('token') === null) return; diff --git a/src/pages/Dashboard/Dashboard.tsx b/src/pages/Dashboard/Dashboard.tsx index 58250cb..990c359 100644 --- a/src/pages/Dashboard/Dashboard.tsx +++ b/src/pages/Dashboard/Dashboard.tsx @@ -237,10 +237,17 @@ export default function Dashboard(): JSX.Element { } }; const handlePvPTake = () => { - dispatch(updateEditorCodeState(GameType.PVP)); + dispatch( + updateEditorCodeState({ gameType: GameType.PVP, language: userLanguage }), + ); }; const handlePvPClose = () => { - dispatch(updateEditorCodeState(GameType.NORMAL)); + dispatch( + updateEditorCodeState({ + gameType: GameType.NORMAL, + language: userLanguage, + }), + ); }; const handleSave = () => { @@ -600,6 +607,7 @@ export default function Dashboard(): JSX.Element { page={pageState} SaveRef={saveButtonRef} SubmitRef={submitButtonRef} + gameType={currentGameType} /> ) : ( { + try { + switch (reqobj.battleTvType) { + case BattleType.NORMAL: + const matchesAPI = new MatchApi(apiConfig); + const normalResponse = await matchesAPI.getUserNormalMatches( + reqobj.page, + ); + // console.log(normalResponse) + return { + battles: normalResponse, + battleType: BattleType.NORMAL, + page: reqobj.page, + }; + case BattleType.PVP: + const matchesAPI1 = new MatchApi(apiConfig); + const pvpResponse = await matchesAPI1.getUserPvPMatches(reqobj.page); + return { + battles: pvpResponse, + battleType: BattleType.PVP, + page: reqobj.page, + }; + case BattleType.DC: + const dcAPI = new DailyChallengesApi(apiConfig); + const dcResponse = await dcAPI.getUserDCMatches(reqobj.page); + return { + battles: dcResponse, + battleType: BattleType.DC, + page: reqobj.page, + }; + } + } catch (error) { + throw rejectWithValue(error); + } + }, +); + const battleTvSlice = createSlice({ name: 'BattleTv', initialState, - reducers: { - getBattleTv: state => { - state.loading = true; - }, - getBattleTvSuccess: (state, { payload }) => { - state.battletv = payload; + reducers: {}, + extraReducers: builder => { + builder.addCase(fetchBattlesAction.fulfilled, (state, { payload }) => { state.loading = false; - state.hasbeenFetched = true; - state.hasErrors = false; - }, - getBattleTvFailure: state => { + switch (payload.battleType) { + case BattleType.NORMAL: + state.Normalbattles.battles = payload.battles as Match[]; + state.Normalbattles.hasbeenFetched = true; + state.Normalbattles.page = payload.page; + break; + case BattleType.PVP: + state.PvPbattles.battles = payload.battles as PvPMatch[]; + state.PvPbattles.hasbeenFetched = true; + state.PvPbattles.page = payload.page; + break; + case BattleType.DC: + state.Dcbattles.battles = payload.battles as Match[]; + state.Dcbattles.hasbeenFetched = true; + state.Dcbattles.page = payload.page; + break; + } + }); + builder.addCase(fetchBattlesAction.rejected, state => { state.loading = false; state.hasErrors = true; - }, + }); + builder.addCase(fetchBattlesAction.pending, state => { + state.loading = true; + }); }, }); -export const { getBattleTv, getBattleTvSuccess, getBattleTvFailure } = - battleTvSlice.actions; - // A selector -export const battleTvSelector = (state: RootState): BattleTvInterFace => - state.battletv; +export const normalBattleTvSelector = (state: RootState): Battles => + state.battletv.Normalbattles; +export const pvpBattleTvSelector = (state: RootState): Battles => + state.battletv.PvPbattles; +export const dcBattleTvSelector = (state: RootState): Battles => + state.battletv.Dcbattles; +export const loadingSelector = (state: RootState): boolean => + state.battletv.loading; -// The reducer -export default battleTvSlice.reducer; - -// Asynchronous thunk action -export function fetchBattleTv(): ThunkAction< - void, - RootState, - unknown, - Action -> { - return async useAppDispatch => { - useAppDispatch(getBattleTv()); +export const hasErrorsSelector = (state: RootState): boolean => + state.battletv.hasErrors; - const matchesAPI = new MatchApi(apiConfig); - matchesAPI - .getUserMatches() - .then(response => { - useAppDispatch(getBattleTvSuccess(response)); - }) - .catch(error => { - if (error instanceof ApiError) Toast.error(error.message); - useAppDispatch(getBattleTvFailure()); - }); - }; -} +export default battleTvSlice.reducer; diff --git a/src/store/editor/code.ts b/src/store/editor/code.ts index 7c93b92..911a997 100644 --- a/src/store/editor/code.ts +++ b/src/store/editor/code.ts @@ -11,8 +11,8 @@ import defaultPvPJavaCode from '../../assets/codes/java/RunPvP.java?raw'; export const languagesAvailable = ['c_cpp', 'python', 'java']; export enum GameType { - NORMAL = 'NORMAL', - PVP = 'PVP', + NORMAL, + PVP, } export interface editorStateType { @@ -42,6 +42,11 @@ export interface CodeAndLanguage { currentUserLanguage: string; } +export interface GameTypeAndLanguage { + gameType: GameType; + language: string; +} + export const editorSlice = createSlice({ name: 'editorState', initialState, @@ -85,17 +90,22 @@ export const editorSlice = createSlice({ state.userCode = newCodeAndLanguage.currentUserCode; }, - updateEditorCodeState: (state, action: PayloadAction) => { - state.gameType = action.payload; - console.log(state); - if (action.payload === GameType.NORMAL) { + updateEditorCodeState: ( + state, + action: PayloadAction, + ) => { + state.gameType = action.payload.gameType; + console.log(action); + if (action.payload.gameType === GameType.NORMAL) { state.userCode = state.allLanguagesNormalCode[ - languagesAvailable.indexOf(state.language) + languagesAvailable.indexOf(action.payload.language) ]; } else { state.userCode = - state.allLanguagesPvPCode[languagesAvailable.indexOf(state.language)]; + state.allLanguagesPvPCode[ + languagesAvailable.indexOf(action.payload.language) + ]; } }, diff --git a/src/store/rendererLogs/logAPI.ts b/src/store/rendererLogs/logAPI.ts index 79d01a7..3d32d83 100644 --- a/src/store/rendererLogs/logAPI.ts +++ b/src/store/rendererLogs/logAPI.ts @@ -6,11 +6,12 @@ export const getLogs = (id: string, gametype: GameType): Promise => { return new Promise((resolve, reject) => { const gameAPI = new GameApi(apiConfig); const pvpGameApi = new PvpGameApi(apiConfig); + console.log(gametype.toString()); if (gametype === GameType.NORMAL) { gameAPI .getGameLogsByGameId(id) .then(logs => { - console.log(logs); + console.log(`fetched ${logs}`); resolve(logs); }) .catch(error => { @@ -22,6 +23,7 @@ export const getLogs = (id: string, gametype: GameType): Promise => { pvpGameApi .getPvpGameLogsByGameId(id) .then(logs => { + console.log(`fetched ${logs}`); resolve(logs); }) .catch(error => { From dc6882875139887a7344add8b1ad50fdfe52d975 Mon Sep 17 00:00:00 2001 From: shubham-1806 Date: Tue, 6 Feb 2024 22:40:18 +0530 Subject: [PATCH 4/8] fix:lint --- src/components/NavBar/NavBar.tsx | 2 -- src/pages/Dashboard/Dashboard.tsx | 3 --- 2 files changed, 5 deletions(-) diff --git a/src/components/NavBar/NavBar.tsx b/src/components/NavBar/NavBar.tsx index 8ed8d04..3a98a80 100644 --- a/src/components/NavBar/NavBar.tsx +++ b/src/components/NavBar/NavBar.tsx @@ -28,7 +28,6 @@ import { changePageState, changeSimulationState, dailyChallengeCompletionState, - dailyChallengePageState, } from '../../store/DailyChallenge/dailyChallenge'; const NavBar: React.FunctionComponent = () => { @@ -38,7 +37,6 @@ const NavBar: React.FunctionComponent = () => { const loggedInUser = useAppSelector(user); const isLogged = useAppSelector(isloggedIn); const loadingAuth = useAppSelector(loading); - const pageState = useAppSelector(dailyChallengePageState); const dcCompletionstatus = useAppSelector(dailyChallengeCompletionState); useEffect(() => { const cookieValue = document.cookie; diff --git a/src/pages/Dashboard/Dashboard.tsx b/src/pages/Dashboard/Dashboard.tsx index 990c359..31d7457 100644 --- a/src/pages/Dashboard/Dashboard.tsx +++ b/src/pages/Dashboard/Dashboard.tsx @@ -72,7 +72,6 @@ import { dcCodeLanguage, dcCode, dcSimulation, - changePageState, } from '../../store/DailyChallenge/dailyChallenge'; import Tour from '../../components/TourProvider/TourProvider'; import { EditorSteps } from '../../components/TourProvider/EditorSteps'; @@ -292,8 +291,6 @@ export default function Dashboard(): JSX.Element { } } - function handlePvPSimulate() {} - const isCommitModalOpen = useAppSelector(IsCommitModalOpen); function handleOpenCommitModal() { From fff35388abff6a08c82c092da650b1e3095abb84 Mon Sep 17 00:00:00 2001 From: shubham-1806 Date: Wed, 7 Feb 2024 23:45:22 +0530 Subject: [PATCH 5/8] fix:pvp fixes --- src/components/BattleTV/BattleTV.tsx | 64 ++++++++++++---------- src/components/CommitModal/CommitModal.tsx | 8 ++- src/components/Leaderboard/Leaderboard.tsx | 9 ++- src/store/BattleTV/BattleTvSlice.ts | 59 +++++++++++++------- 4 files changed, 86 insertions(+), 54 deletions(-) diff --git a/src/components/BattleTV/BattleTV.tsx b/src/components/BattleTV/BattleTV.tsx index ee3f3ea..cfa62c9 100644 --- a/src/components/BattleTV/BattleTV.tsx +++ b/src/components/BattleTV/BattleTV.tsx @@ -1,5 +1,4 @@ import { useEffect, useState } from 'react'; -import ReactPaginate from 'react-paginate'; import { getLogAction } from '../../store/rendererLogs/logSlice'; import { useNavigate } from 'react-router-dom'; import styles from './BattleTV.module.css'; @@ -88,7 +87,6 @@ function getUsersGame( } function PaginatedItems({ battleTvType }: { battleTvType: BattleType }) { - const [pageCount, setPageCount] = useState(0); const navigate = useNavigate(); const currentBattles = battleTvType == BattleType.PVP @@ -99,25 +97,17 @@ function PaginatedItems({ battleTvType }: { battleTvType: BattleType }) { const loading = useAppSelector(loadingSelector); const loggedInUser = useAppSelector(user); const hasErrors = useAppSelector(hasErrorsSelector); + const pageCount = currentBattles.page; // initialize the redux hook const dispatch = useAppDispatch(); if (!currentBattles.hasbeenFetched) { - if (currentBattles.page !== pageCount) { - dispatch( - fetchBattlesAction({ battleTvType: battleTvType, page: pageCount }), - ); - } - } - - const handlePageClick = (event: { selected: number }) => { - setPageCount(event.selected); + console.log('fetching battles'); dispatch( - fetchBattlesAction({ battleTvType: battleTvType, page: event.selected }), + fetchBattlesAction({ battleTvType: battleTvType, page: pageCount }), ); - console.log('changed'); - }; + } return ( <> @@ -237,21 +227,37 @@ function PaginatedItems({ battleTvType }: { battleTvType: BattleType }) { )}
RANK USERNAMERATINGSWONLOSTTIEDSCORE
- {items.indexOf(row) + 1 + page * itemsPerPage} + {dcItems.indexOf(row) + 1 + page * itemsPerPage}
- - {' ' + row.user.username.substring(0, 10)} - + {' ' + row.userName.substring(0, 10)}
- {row.stats.rating.toFixed(3)} - --- handleShow(row.user.username)} - > - - {row.stats.wins}{row.stats.losses}{row.stats.ties}{row.score.toFixed(2)}