From 4e18827b62998f327bc9ea58b596b7e3eb22aa19 Mon Sep 17 00:00:00 2001 From: Quildra Date: Wed, 20 Dec 2023 21:35:36 +0000 Subject: [PATCH] Add claiming a racer --- .../src/upload/upload.controller.ts | 20 ++++- .../bridge-server/src/upload/upload.module.ts | 2 + packages/bridge-ui/src/app/app.config.ts | 33 +-------- .../claim-racer-dialog.component.html | 17 +++++ .../claim-racer-dialog.component.scss | 16 ++++ .../claim-racer-dialog.component.spec.ts | 23 ++++++ .../claim-racer-dialog.component.ts | 73 +++++++++++++++++++ .../src/app/pages/user/user.component.html | 2 +- .../src/app/pages/user/user.component.ts | 9 +++ .../src/app/services/replays.service.ts | 6 ++ 10 files changed, 166 insertions(+), 35 deletions(-) create mode 100644 packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.html create mode 100644 packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.scss create mode 100644 packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.spec.ts create mode 100644 packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.ts diff --git a/packages/bridge-server/src/upload/upload.controller.ts b/packages/bridge-server/src/upload/upload.controller.ts index b21b680..36720de 100644 --- a/packages/bridge-server/src/upload/upload.controller.ts +++ b/packages/bridge-server/src/upload/upload.controller.ts @@ -1,14 +1,17 @@ -import { Controller, Post, UseInterceptors } from '@nestjs/common'; +import { Controller, NotAcceptableException, Post, UseInterceptors, Request } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; -import { Body, UploadedFile } from '@nestjs/common/decorators'; +import { Body, UploadedFile, UseGuards } from '@nestjs/common/decorators'; import { Express } from 'express' import { UploadService } from './upload.service'; +import { JwtAuthGuard } from 'src/authz/authz.guard'; +import { UsersService } from 'src/users/users.service'; @Controller('upload') export class UploadController { constructor( - private uploadService: UploadService + private uploadService: UploadService, + private usersService: UsersService ){} @Post('replay') @@ -16,4 +19,15 @@ export class UploadController { uploadFile(@UploadedFile() file: Express.Multer.File, @Body() body: any) { return this.uploadService.replayUploaded(file, body); } + + @UseGuards(JwtAuthGuard) + @Post('claim-racer') + @UseInterceptors(FileInterceptor('file')) + async claimRacer(@UploadedFile() file: Express.Multer.File, @Body() body: any, @Request() req) { + let racer = await this.uploadService.getRacerFromReplay(file); + if(!racer) { + throw new NotAcceptableException('NoRacerFound', {cause: new Error(), description: 'No existing racer found.'}); + } + return this.usersService.claimRacer(req.user, racer); + } } diff --git a/packages/bridge-server/src/upload/upload.module.ts b/packages/bridge-server/src/upload/upload.module.ts index 904a4da..2d31b15 100644 --- a/packages/bridge-server/src/upload/upload.module.ts +++ b/packages/bridge-server/src/upload/upload.module.ts @@ -5,6 +5,7 @@ import { RacersModule } from 'src/racers/racers.module'; import { RacesModule } from 'src/races/races.module'; import { SeasonStandingsModule } from 'src/season-standings/season-standings.module'; import { RaceResultsModule } from 'src/race-results/race-results.module'; +import { UsersModule } from 'src/users/users.module'; @Module({ imports:[ @@ -12,6 +13,7 @@ import { RaceResultsModule } from 'src/race-results/race-results.module'; RacesModule, RaceResultsModule, SeasonStandingsModule, + UsersModule ], providers: [UploadService], controllers: [UploadController], diff --git a/packages/bridge-ui/src/app/app.config.ts b/packages/bridge-ui/src/app/app.config.ts index 1011b5f..9eac268 100644 --- a/packages/bridge-ui/src/app/app.config.ts +++ b/packages/bridge-ui/src/app/app.config.ts @@ -32,40 +32,11 @@ export const appConfig: ApplicationConfig = { { uriMatcher: (uri) => { let is_create = uri.match('.+\/create'); - return is_create != null; - }, - tokenOptions: { - authorizationParams: { - audience: 'https://ponyta.pkmn.cloud' - } - } - }, - { - uriMatcher: (uri) => { let is_update = uri.match('.+\/update'); - return is_update != null; - }, - tokenOptions: { - authorizationParams: { - audience: 'https://ponyta.pkmn.cloud' - } - } - }, - { - uriMatcher: (uri) => { let is_delete = uri.match('.+\/delete'); - return is_delete != null; - }, - tokenOptions: { - authorizationParams: { - audience: 'https://ponyta.pkmn.cloud' - } - } - }, - { - uriMatcher: (uri) => { let is_login = uri.match('.+\/login'); - return is_login != null; + let is_claim = uri.match('.+\/claim-racer'); + return is_create != null || is_update != null || is_delete != null || is_claim != null; }, tokenOptions: { authorizationParams: { diff --git a/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.html b/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.html new file mode 100644 index 0000000..23b53c4 --- /dev/null +++ b/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.html @@ -0,0 +1,17 @@ +
+ +

Replay Upload

+

Replay files can be found in Documents/Trackmania/Replays/My Replays

+
+ File + {{fileName || "No file uploaded yet."}} + +
+ + + + +
diff --git a/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.scss b/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.scss new file mode 100644 index 0000000..b1bc93e --- /dev/null +++ b/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.scss @@ -0,0 +1,16 @@ +.file-input { + display: none; +} + +.signInForm { + width: 400px; + padding: 12px 24px 24px; + + igx-input-group + igx-input-group { + margin-top: 24px; + } + } + + .full-width { + width: 100%; + } \ No newline at end of file diff --git a/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.spec.ts b/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.spec.ts new file mode 100644 index 0000000..e92b1ce --- /dev/null +++ b/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ClaimRacerDialogComponent } from './claim-racer-dialog.component'; + +describe('ClaimRacerDialogComponent', () => { + let component: ClaimRacerDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ClaimRacerDialogComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ClaimRacerDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.ts b/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.ts new file mode 100644 index 0000000..2fe36fa --- /dev/null +++ b/packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.ts @@ -0,0 +1,73 @@ +import { Component, Inject } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ReactiveFormsModule, FormControl } from '@angular/forms'; +import { FormsModule } from '@angular/forms'; + +import { MatDialog, MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, + MatDialogContent, MatDialogActions, MatDialogClose,} from '@angular/material/dialog'; + +import { MatButtonModule } from '@angular/material/button'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatInputModule } from '@angular/material/input'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatSnackBar } from '@angular/material/snack-bar'; + +import { ReplaysService } from '../../services/replays.service'; +import { catchError, throwError } from 'rxjs'; +import { HttpErrorResponse } from '@angular/common/http'; + +@Component({ + selector: 'app-claim-racer-dialog', + standalone: true, + imports: [ + CommonModule, + ReactiveFormsModule, + MatButtonModule, + MatDialogModule, + MatInputModule, + MatFormFieldModule, + FormsModule, + MatDialogTitle, + MatDialogContent, + MatDialogActions, + MatDialogClose, + MatIconModule, + ], + templateUrl: './claim-racer-dialog.component.html', + styleUrl: './claim-racer-dialog.component.scss' +}) +export class ClaimRacerDialogComponent { + fileName: string = ""; + file?: File; + errors: string[]; + + constructor( + private replaysService: ReplaysService, + private _snackBar: MatSnackBar + ) { + this.errors = []; + } + + onFileSelected(event: any) { + const file: File = event.target.files[0]; + if (file) + { + this.fileName = file.name; + this.file = file; + } + } + + onClickSubmit() { + if(this.file == undefined) { + return; + } + + let formData = new FormData(); + formData.append("file", this.file); + let filetime = this.file.lastModified; + formData.append("fileTime", filetime.toString()); + const upload$ = this.replaysService.claimRacer(formData); + upload$.subscribe(); + } +} diff --git a/packages/bridge-ui/src/app/pages/user/user.component.html b/packages/bridge-ui/src/app/pages/user/user.component.html index a1a107a..a62fe8d 100644 --- a/packages/bridge-ui/src/app/pages/user/user.component.html +++ b/packages/bridge-ui/src/app/pages/user/user.component.html @@ -22,7 +22,7 @@ - + diff --git a/packages/bridge-ui/src/app/pages/user/user.component.ts b/packages/bridge-ui/src/app/pages/user/user.component.ts index c8dff83..00bb9fb 100644 --- a/packages/bridge-ui/src/app/pages/user/user.component.ts +++ b/packages/bridge-ui/src/app/pages/user/user.component.ts @@ -4,8 +4,11 @@ import { CommonModule } from '@angular/common'; import { MatCardModule } from '@angular/material/card'; import { MatIconModule } from '@angular/material/icon'; import { MatButtonModule } from '@angular/material/button'; +import { MatDialog } from '@angular/material/dialog'; import { UsersService } from '../../services/users.service'; +import { ClaimRacerDialogComponent } from '../../components/claim-racer-dialog/claim-racer-dialog.component'; + @Component({ selector: 'app-user', @@ -22,5 +25,11 @@ import { UsersService } from '../../services/users.service'; export class UserComponent { constructor( public usersService: UsersService, + private dialog: MatDialog, ) {} + + openClaimRacerDialog() + { + this.dialog.open(ClaimRacerDialogComponent); + } } diff --git a/packages/bridge-ui/src/app/services/replays.service.ts b/packages/bridge-ui/src/app/services/replays.service.ts index e084281..1ff8bd5 100644 --- a/packages/bridge-ui/src/app/services/replays.service.ts +++ b/packages/bridge-ui/src/app/services/replays.service.ts @@ -19,4 +19,10 @@ export class ReplaysService { headers.append('Content-Type', 'multipart/form-data'); return this.httpClient.post(this.serverEndpointService.getCurrentEndpoint()+"upload/replay", newReplay, { headers }); } + + claimRacer(newReplay: FormData) { + const headers = new HttpHeaders(); + headers.append('Content-Type', 'multipart/form-data'); + return this.httpClient.post(this.serverEndpointService.getCurrentEndpoint()+"upload/claim-racer", newReplay, { headers }); + } }