121 lines
4.6 KiB
TypeScript
121 lines
4.6 KiB
TypeScript
import type { DriverRatingProvider } from '@core/racing/application/ports/DriverRatingProvider';
|
|
import type { ImageServicePort } from '@core/racing/application/ports/ImageServicePort';
|
|
import type { GetRaceDetailResult } from '@core/racing/application/use-cases/GetRaceDetailUseCase';
|
|
import type { GetRaceDetailParamsDTO } from '../dtos/GetRaceDetailParamsDTO';
|
|
import type { RaceDetailDTO } from '../dtos/RaceDetailDTO';
|
|
import type { RaceDetailEntryDTO } from '../dtos/RaceDetailEntryDTO';
|
|
import type { RaceDetailLeagueDTO } from '../dtos/RaceDetailLeagueDTO';
|
|
import type { RaceDetailRaceDTO } from '../dtos/RaceDetailRaceDTO';
|
|
import type { RaceDetailRegistrationDTO } from '../dtos/RaceDetailRegistrationDTO';
|
|
import type { RaceDetailUserResultDTO } from '../dtos/RaceDetailUserResultDTO';
|
|
|
|
export type GetRaceDetailResponseModel = RaceDetailDTO;
|
|
|
|
export class RaceDetailPresenter {
|
|
private result: GetRaceDetailResult | null = null;
|
|
private params: GetRaceDetailParamsDTO | null = null;
|
|
|
|
constructor(
|
|
private readonly driverRatingProvider: DriverRatingProvider,
|
|
private readonly imageService: ImageServicePort,
|
|
) {}
|
|
|
|
present(result: GetRaceDetailResult, params: GetRaceDetailParamsDTO): void {
|
|
this.result = result;
|
|
this.params = params;
|
|
}
|
|
|
|
async getResponseModel(): Promise<GetRaceDetailResponseModel | null> {
|
|
if (!this.result || !this.params) {
|
|
return null;
|
|
}
|
|
|
|
const output = this.result;
|
|
const params = this.params;
|
|
|
|
const raceDTO: RaceDetailRaceDTO | null = output.race
|
|
? {
|
|
id: output.race.id,
|
|
leagueId: output.race.leagueId,
|
|
track: output.race.track,
|
|
car: output.race.car,
|
|
scheduledAt: output.race.scheduledAt.toISOString(),
|
|
sessionType: output.race.sessionType.props,
|
|
status: output.race.status.toString(),
|
|
strengthOfField: output.race.strengthOfField?.toNumber() ?? null,
|
|
...(output.race.registeredCount !== undefined && {
|
|
registeredCount: output.race.registeredCount.toNumber(),
|
|
}),
|
|
...(output.race.maxParticipants !== undefined && {
|
|
maxParticipants: output.race.maxParticipants.toNumber(),
|
|
}),
|
|
}
|
|
: null;
|
|
|
|
const leagueDTO: RaceDetailLeagueDTO | null = output.league
|
|
? {
|
|
id: output.league.id.toString(),
|
|
name: output.league.name.toString(),
|
|
description: output.league.description.toString(),
|
|
settings: {
|
|
...(output.league.settings.maxDrivers !== undefined && { maxDrivers: output.league.settings.maxDrivers }),
|
|
...(output.league.settings.qualifyingFormat !== undefined && { qualifyingFormat: output.league.settings.qualifyingFormat.toString() }),
|
|
},
|
|
}
|
|
: null;
|
|
|
|
const entryListDTO: RaceDetailEntryDTO[] = await Promise.all(
|
|
output.drivers.map(async driver => {
|
|
const rating = this.driverRatingProvider.getRating(driver.id);
|
|
const avatarUrl = this.imageService.getDriverAvatar(driver.id);
|
|
return {
|
|
id: driver.id,
|
|
name: driver.name.toString(),
|
|
country: driver.country.toString(),
|
|
avatarUrl,
|
|
rating,
|
|
isCurrentUser: driver.id === params.driverId,
|
|
};
|
|
}),
|
|
);
|
|
|
|
const registrationDTO: RaceDetailRegistrationDTO = {
|
|
isUserRegistered: output.isUserRegistered,
|
|
canRegister: output.canRegister,
|
|
};
|
|
|
|
const userResultDTO: RaceDetailUserResultDTO | null = output.userResult
|
|
? {
|
|
position: output.userResult.position.toNumber(),
|
|
startPosition: output.userResult.startPosition.toNumber(),
|
|
incidents: output.userResult.incidents.toNumber(),
|
|
fastestLap: output.userResult.fastestLap.toNumber(),
|
|
positionChange: output.userResult.getPositionChange(),
|
|
isPodium: output.userResult.isPodium(),
|
|
isClean: output.userResult.isClean(),
|
|
ratingChange: this.calculateRatingChange(output.userResult.position.toNumber()),
|
|
}
|
|
: null;
|
|
|
|
return {
|
|
race: raceDTO,
|
|
league: leagueDTO,
|
|
entryList: entryListDTO,
|
|
registration: registrationDTO,
|
|
userResult: userResultDTO,
|
|
} as RaceDetailDTO;
|
|
}
|
|
|
|
get viewModel(): Promise<GetRaceDetailResponseModel> {
|
|
return this.getResponseModel().then(model => {
|
|
if (!model) throw new Error('Presenter not presented');
|
|
return model;
|
|
});
|
|
}
|
|
|
|
private calculateRatingChange(position: number): number {
|
|
const baseChange = position <= 3 ? 25 : position <= 10 ? 10 : -5;
|
|
const positionBonus = Math.max(0, (20 - position) * 2);
|
|
return baseChange + positionBonus;
|
|
}
|
|
} |