Browse Source

Add claiming a racer

feature/mapper-integration
Quildra 2 years ago
parent
commit
4e18827b62
  1. 20
      packages/bridge-server/src/upload/upload.controller.ts
  2. 2
      packages/bridge-server/src/upload/upload.module.ts
  3. 33
      packages/bridge-ui/src/app/app.config.ts
  4. 17
      packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.html
  5. 16
      packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.scss
  6. 23
      packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.spec.ts
  7. 73
      packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.ts
  8. 2
      packages/bridge-ui/src/app/pages/user/user.component.html
  9. 9
      packages/bridge-ui/src/app/pages/user/user.component.ts
  10. 6
      packages/bridge-ui/src/app/services/replays.service.ts

20
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);
}
}

2
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],

33
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: {

17
packages/bridge-ui/src/app/components/claim-racer-dialog/claim-racer-dialog.component.html

@ -0,0 +1,17 @@
<div class="signInForm">
<input type="file" accept=".gbx" class="file-input" (change)="onFileSelected($event)" #fileUpload>
<h1 mat-dialog-title>Replay Upload</h1>
<p>Replay files can be found in Documents/Trackmania/Replays/My Replays</p>
<div mat-dialog-content class="full-width">
<mat-label>File</mat-label>
{{fileName || "No file uploaded yet."}}
<button mat-fab color="primary" class="upload-btn"
(click)="fileUpload.click()">
<mat-icon>attach_file</mat-icon>
</button>
</div>
<mat-dialog-actions align="end">
<button mat-button mat-dialog-close>Cancel</button>
<button mat-button mat-dialog-close (click)="onClickSubmit()">Submit</button>
</mat-dialog-actions>
</div>

16
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%;
}

23
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<ClaimRacerDialogComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ClaimRacerDialogComponent]
})
.compileComponents();
fixture = TestBed.createComponent(ClaimRacerDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

73
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();
}
}

2
packages/bridge-ui/src/app/pages/user/user.component.html

@ -22,7 +22,7 @@
</mat-card-content>
<mat-card-actions>
<button mat-button>Claim</button>
<button mat-button color="accent" (click)="openClaimRacerDialog()">Claim</button>
</mat-card-actions>
</mat-card>
</div>

9
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);
}
}

6
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 });
}
}

Loading…
Cancel
Save