Skip to content

Commit

Permalink
Merge pull request #67 from game-node-app/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
Lamarcke authored Apr 5, 2024
2 parents 5f3f004 + b79d102 commit 4f51362
Show file tree
Hide file tree
Showing 15 changed files with 171 additions and 53 deletions.
Binary file added public/icons/twitch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion server_swagger.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions src/achievements/achievements.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export class AchievementsController {
@Param("id") achievementId: string,
@Query() queryDto: GetObtainedAchievementRequestDto,
) {
return this.achievementsService.getObtainedAchievementById(
return this.achievementsService.getObtainedAchievementByAchievementId(
queryDto.targetUserId,
achievementId,
);
Expand All @@ -59,13 +59,15 @@ export class AchievementsController {
);
}

@Put("featured")
@Put("obtained/:id/featured")
public updateFeaturedObtainedAchievement(
@Session() session: SessionContainer,
@Param("id") achievementId: string,
@Body() dto: UpdateFeaturedObtainedAchievementDto,
) {
return this.achievementsService.updateFeaturedObtainedAchievement(
session.getUserId(),
achievementId,
dto,
);
}
Expand Down
42 changes: 24 additions & 18 deletions src/achievements/achievements.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export class AchievementsService {
const shouldSkipAchievement =
await this.obtainedAchievementsRepository.exists({
where: {
id: achievement.id,
achievementId: achievement.id,
profile: {
userId: targetUserId,
},
Expand All @@ -87,12 +87,12 @@ export class AchievementsService {
if (!isEligible) return;

const obtainedAchievementEntity =
this.obtainedAchievementsRepository.create();
obtainedAchievementEntity.id = achievement.id;
obtainedAchievementEntity.profile = {
userId: targetUserId,
} as Profile;

this.obtainedAchievementsRepository.create({
achievementId: achievement.id,
profile: {
userId: targetUserId,
} as Profile,
});
await this.obtainedAchievementsRepository.save(
obtainedAchievementEntity,
);
Expand Down Expand Up @@ -121,7 +121,7 @@ export class AchievementsService {
}
}

async getObtainedAchievementById(
async getObtainedAchievementByAchievementId(
targetUserId: string,
achievementId: string,
) {
Expand All @@ -133,13 +133,13 @@ export class AchievementsService {
profile: {
userId: targetUserId,
},
id: achievementId,
achievementId: achievementId,
},
);

if (achievement) return achievement;

throw new HttpException("", 404);
throw new HttpException("User has not obtained this achievement.", 404);
}

async getObtainedAchievementsByUserId(targetUserId: string) {
Expand All @@ -152,24 +152,30 @@ export class AchievementsService {

async updateFeaturedObtainedAchievement(
userId: string,
achievementId: string,
dto: UpdateFeaturedObtainedAchievementDto,
) {
const entity = await this.getObtainedAchievementById(userId, dto.id);
const updatedEntity = this.obtainedAchievementsRepository.merge(
entity,
dto,
const entity = await this.getObtainedAchievementByAchievementId(
userId,
achievementId,
);
await this.obtainedAchievementsRepository.update(
{
achievementId: entity.achievementId,
profileUserId: userId,
},
{
isFeatured: dto.isFeatured,
},
);
await this.obtainedAchievementsRepository.save(updatedEntity);
/**
* Disables isFeatured flag of any other obtained achievement
*/
if (dto.isFeatured) {
await this.obtainedAchievementsRepository.update(
{
id: Not(entity.id),
profile: {
userId,
},
profileUserId: userId,
isFeatured: true,
},
{
Expand Down
12 changes: 6 additions & 6 deletions src/achievements/dto/update-featured-obtained-achievement.dto.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PickType } from "@nestjs/swagger";
import { ObtainedAchievement } from "../entities/obtained-achievement.entity";
import { IsBoolean, IsNotEmpty } from "class-validator";

export class UpdateFeaturedObtainedAchievementDto extends PickType(
ObtainedAchievement,
["id", "isFeatured"],
) {}
export class UpdateFeaturedObtainedAchievementDto {
@IsNotEmpty()
@IsBoolean()
isFeatured: boolean;
}
24 changes: 21 additions & 3 deletions src/achievements/entities/obtained-achievement.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import {
Column,
CreateDateColumn,
Entity,
Index,
ManyToOne,
PrimaryColumn,
PrimaryGeneratedColumn,
Unique,
UpdateDateColumn,
} from "typeorm";
import { Profile } from "../../profile/entities/profile.entity";
Expand All @@ -12,11 +15,26 @@ import { Profile } from "../../profile/entities/profile.entity";
* Entity to track a user's obtained achievements
*/
@Entity()
@Unique(["achievementId", "profile"])
export class ObtainedAchievement {
@PrimaryColumn()
id: string;
@ManyToOne(() => Profile)
@PrimaryGeneratedColumn()
id: number;
/**
* Achievement id specified in entries for achievements.data.ts
*/
@Column({
nullable: false,
length: 36,
})
achievementId: string;
@ManyToOne(() => Profile, {
nullable: false,
})
profile: Profile;
@Column({
nullable: false,
})
profileUserId: string;
@Column({
default: false,
})
Expand Down
15 changes: 8 additions & 7 deletions src/game/game-repository/game-repository.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,15 @@ const platformAbbreviationToIconMap: { [p: string]: string[] } = {

const externalGameCategoryToIconMap: { [p: string]: number[] } = {
xbox: [
EGameExternalGameCategory.XboxMarketplace.valueOf(),
EGameExternalGameCategory.XboxGamePassUltimateCloud.valueOf(),
EGameExternalGameCategory.Microsoft.valueOf(),
EGameExternalGameCategory.XboxMarketplace,
EGameExternalGameCategory.XboxGamePassUltimateCloud,
EGameExternalGameCategory.Microsoft,
],
steam: [EGameExternalGameCategory.Steam.valueOf()],
epicgames: [EGameExternalGameCategory.EpicGamesStore.valueOf()],
android: [EGameExternalGameCategory.Android.valueOf()],
playstation: [EGameExternalGameCategory.PlaystationStoreUs.valueOf()],
steam: [EGameExternalGameCategory.Steam],
epicgames: [EGameExternalGameCategory.EpicGamesStore],
android: [EGameExternalGameCategory.Android],
playstation: [EGameExternalGameCategory.PlaystationStoreUs],
twitch: [EGameExternalGameCategory.Twitch],
};

export {
Expand Down
10 changes: 9 additions & 1 deletion src/game/game-repository/game-repository.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,15 @@ export class GameRepositoryService {
HttpStatus.NOT_FOUND,
);
}
const externalStoreDtos = externalGames.map((externalGame) => {

const uniqueExternalGamesMap = externalGames.reduce((acc, current) => {
if (current.category) {
acc.set(current.category, current);
}
return acc;
}, new Map<number, GameExternalGame>());
const uniqueExternalGames = Array.from(uniqueExternalGamesMap.values());
const externalStoreDtos = uniqueExternalGames.map((externalGame) => {
const icon = getIconNameForExternalGameCategory(
externalGame.category?.valueOf(),
);
Expand Down
2 changes: 2 additions & 0 deletions src/game/game-repository/game-repository.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export function getStoreNameForExternalGameCategory(
return "Microsoft Store";
case EGameExternalGameCategory.PlaystationStoreUs:
return "Playstation Store";
case EGameExternalGameCategory.Twitch:
return "Twitch";
}

return null;
Expand Down
13 changes: 13 additions & 0 deletions src/level/level.constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
export const BASE_LEVEL_UP_COST = 100;

/**
* Activities that automatically grant some EXP when performed.
* It's important to make sure these activities won't make it simple
* for users to exploit the leveling process.
*/
export enum LevelIncreaseActivities {
REVIEW_CREATED = "review_created",
}

export const LevelActivitiesToIncreaseAmountMap = {
[LevelIncreaseActivities.REVIEW_CREATED]: 50,
};
26 changes: 24 additions & 2 deletions src/level/level.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { HttpException, Injectable } from "@nestjs/common";
import { HttpException, Injectable, Logger } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { UserLevel } from "./entities/user-level.entity";
import { Repository } from "typeorm";
import { BASE_LEVEL_UP_COST } from "./level.constants";
import {
BASE_LEVEL_UP_COST,
LevelActivitiesToIncreaseAmountMap,
LevelIncreaseActivities,
} from "./level.constants";

@Injectable()
export class LevelService {
private readonly logger = new Logger(LevelService.name);
private readonly currentMaximumLevel = 50;
/**
* The base amount to multiply the current user-level requirement when a user levels up
Expand Down Expand Up @@ -79,6 +84,23 @@ export class LevelService {
await this.userLevelRepository.save(updatedUserLevelEntity);
}

registerLevelExpIncreaseActivity(
userId: string,
activity: LevelIncreaseActivities,
) {
const activityIncreaseAmount =
LevelActivitiesToIncreaseAmountMap[activity];
if (activityIncreaseAmount == undefined) {
throw new Error("Invalid activity when registering level increase");
}

this.increaseExp(userId, activityIncreaseAmount)
.then()
.catch((err) => {
this.logger.error(err);
});
}

/**
* Retrieves the levelUpExpCost to reach a new level based on the provided level.
* @param level
Expand Down
2 changes: 2 additions & 0 deletions src/reviews/reviews.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { CollectionEntry } from "../collections/collections-entries/entities/col
import { CollectionsEntriesModule } from "../collections/collections-entries/collections-entries.module";
import { AchievementsModule } from "../achievements/achievements.module";
import { StatisticsQueueModule } from "../statistics/statistics-queue/statistics-queue.module";
import { LevelModule } from "../level/level.module";

@Module({
imports: [
Expand All @@ -18,6 +19,7 @@ import { StatisticsQueueModule } from "../statistics/statistics-queue/statistics
AchievementsModule,
CollectionsEntriesModule,
StatisticsQueueModule,
LevelModule,
],
controllers: [ReviewsController],
providers: [ReviewsService],
Expand Down
16 changes: 9 additions & 7 deletions src/reviews/reviews.service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import {
forwardRef,
HttpException,
HttpStatus,
Inject,
Logger,
} from "@nestjs/common";
import { HttpException, HttpStatus, Logger } from "@nestjs/common";
import { CreateReviewDto } from "./dto/create-review.dto";
import { InjectRepository } from "@nestjs/typeorm";
import { Review } from "./entities/review.entity";
Expand All @@ -23,6 +17,8 @@ import {
ReviewScoreResponseDto,
} from "./dto/review-score-response.dto";
import { StatisticsQueueService } from "../statistics/statistics-queue/statistics-queue.service";
import { LevelService } from "../level/level.service";
import { LevelIncreaseActivities } from "../level/level.constants";

export class ReviewsService {
private readonly logger = new Logger(ReviewsService.name);
Expand All @@ -34,6 +30,7 @@ export class ReviewsService {
private collectionsEntriesService: CollectionsEntriesService,
private readonly achievementsQueueService: AchievementsQueueService,
private readonly statisticsQueueService: StatisticsQueueService,
private readonly levelService: LevelService,
) {}

async findOneById(id: string) {
Expand Down Expand Up @@ -208,6 +205,11 @@ export class ReviewsService {
sourceType: StatisticsSourceType.REVIEW,
sourceId: insertedEntry.id,
});

this.levelService.registerLevelExpIncreaseActivity(
userId,
LevelIncreaseActivities.REVIEW_CREATED,
);
}

async delete(userId: string, reviewId: string) {
Expand Down
20 changes: 19 additions & 1 deletion src/statistics/dto/find-statistics-trending-reviews.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { IsEnum, IsNotEmpty, IsNumber, IsOptional } from "class-validator";
import {
IsEnum,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
Length,
} from "class-validator";
import { BaseFindDto } from "../../utils/base-find.dto";
import { Statistics } from "../entity/statistics.entity";
import { StatisticsPeriod } from "../statistics.constants";
Expand All @@ -8,9 +15,20 @@ export class FindStatisticsTrendingReviewsDto extends OmitType(
BaseFindDto<Statistics>,
["orderBy", "search"],
) {
/**
* Usually, this property should not be used unless a specific review needs to be retrieved, and it's easier to just
* call the statistics controller.
*/
@IsOptional()
@IsString()
reviewId?: string;
@IsOptional()
@IsNumber()
gameId?: number;
@IsOptional()
@IsString()
@Length(36)
userId?: string;
@IsNotEmpty()
@IsEnum(StatisticsPeriod)
period: StatisticsPeriod;
Expand Down
Loading

0 comments on commit 4f51362

Please sign in to comment.