diff --git a/src/assets/league-tier-backgrounds/diamond_tournament_1.svg b/src/assets/league-tier-backgrounds/diamond_tournament_1.svg new file mode 100644 index 0000000..d747d3c --- /dev/null +++ b/src/assets/league-tier-backgrounds/diamond_tournament_1.svg @@ -0,0 +1,32 @@ + + Diamond Tournament 1 + Created by Duolingo. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/league-tier-backgrounds/diamond_tournament_2.svg b/src/assets/league-tier-backgrounds/diamond_tournament_2.svg new file mode 100644 index 0000000..13fde48 --- /dev/null +++ b/src/assets/league-tier-backgrounds/diamond_tournament_2.svg @@ -0,0 +1,3 @@ + + Diamond Tournament 2 Placeholder + diff --git a/src/assets/league-tier-backgrounds/diamond_tournament_3.svg b/src/assets/league-tier-backgrounds/diamond_tournament_3.svg new file mode 100644 index 0000000..31a4cd3 --- /dev/null +++ b/src/assets/league-tier-backgrounds/diamond_tournament_3.svg @@ -0,0 +1,3 @@ + + Diamond Tournament 3 Placeholder + diff --git a/src/entities/models/user.ts b/src/entities/models/user.ts index f4418f1..5f9fe49 100644 --- a/src/entities/models/user.ts +++ b/src/entities/models/user.ts @@ -10,12 +10,12 @@ export class User { public static fromResponse( response: UserDto, - leaderboardTier?: number, + leaderboardInfo?: LeaderBoardInfo, ): User { - return new User(response, leaderboardTier); + return new User(response, leaderboardInfo); } - private constructor(user: UserDto, leaderboardTier?: number) { + private constructor(user: UserDto, leaderboardInfo?: LeaderBoardInfo) { this.name = user.name; this.username = user.username; this.pictureUrl = user.picture.replace("//", "https://") + "/xxlarge"; @@ -26,8 +26,8 @@ export class User { if (currentCourse) { this.currentCourse = CurrentCourse.fromResponse(currentCourse); } - if (leaderboardTier !== undefined && this.streak.days > 0) { - this.currentLeaderboardTier = mapLeaderboardTier(leaderboardTier); + if (leaderboardInfo !== undefined && this.streak.days > 0) { + this.currentLeaderboardTier = mapLeaderboardTier(leaderboardInfo); } } } @@ -81,9 +81,28 @@ class CurrentCourse { } } -const mapLeaderboardTier = (tier: number): Tier => { - // Map the leaderboard tier to the enum value, any value higher than the max tier will be mapped to the max tier. - return Math.min(tier, Tier.DIAMOND); +const mapLeaderboardTier = (leaderBoardInfo: LeaderBoardInfo): Tier => { + let tier = Math.min(leaderBoardInfo.tier, Tier.DIAMOND); + + if (tier >= Tier.DIAMOND) { + switch (leaderBoardInfo.numWins ?? 0) { + case 1: + tier = Tier.DIAMOND_TOURNAMENT_1; + break; + case 2: + tier = Tier.DIAMOND_TOURNAMENT_2; + break; + case 3: + tier = Tier.DIAMOND_TOURNAMENT_3; + break; + case 0: + default: + tier = Tier.DIAMOND; + break; + } + } + + return tier; }; export enum Tier { @@ -97,4 +116,12 @@ export enum Tier { PEARL = 7, OBSIDIAN = 8, DIAMOND = 9, + DIAMOND_TOURNAMENT_1 = 10, + DIAMOND_TOURNAMENT_2 = 11, + DIAMOND_TOURNAMENT_3 = 12, } + +export type LeaderBoardInfo = { + tier: number; + numWins: number; +}; diff --git a/src/hooks/useUserData.ts b/src/hooks/useUserData.ts index 72655fd..d757ef2 100644 --- a/src/hooks/useUserData.ts +++ b/src/hooks/useUserData.ts @@ -1,7 +1,7 @@ import { useQuery } from "@tanstack/react-query"; import { fetchWithProxy } from "../utilities"; import { DuolingoUserResponse, UserDto } from "../entities/responses"; -import { User } from "../entities/models"; +import { LeaderBoardInfo, User } from "../entities/models"; import { DuolingoLeaderboardResponse } from "../entities/responses/duolingo-leaderboard-response"; /** @@ -44,15 +44,18 @@ const fetchUser = async (userName: string): Promise => { const fetchLeaderboardTier = async ( userId: number, -): Promise => { +): Promise => { try { const rawUserResponse = await fetchWithProxy( `https://duolingo-leaderboards-prod.duolingo.com/leaderboards/7d9f5dd1-8423-491a-91f2-2532052038ce/users/${userId}`, ); const typedUserResponse = (await rawUserResponse.json()) as DuolingoLeaderboardResponse; - const leaderboardTier = typedUserResponse.tier; - return leaderboardTier; + + return { + tier: typedUserResponse.tier, + numWins: typedUserResponse.stats.num_wins, + }; } catch (error) { console.error("Error fetching leaderboard tier", error); return undefined; diff --git a/src/utilities/league-tier-background/determine-league-background.ts b/src/utilities/league-tier-background/determine-league-background.ts index 2d952c0..048fd07 100644 --- a/src/utilities/league-tier-background/determine-league-background.ts +++ b/src/utilities/league-tier-background/determine-league-background.ts @@ -9,32 +9,47 @@ import leagueAmethystSvg from "../../assets/league-tier-backgrounds/amethyst.svg import leaguePearlSvg from "../../assets/league-tier-backgrounds/pearl.svg"; import leagueObsidianSvg from "../../assets/league-tier-backgrounds/obsidian.svg"; import leagueDiamondSvg from "../../assets/league-tier-backgrounds/diamond.svg"; +import leagueDiamondTournament1Svg from "../../assets/league-tier-backgrounds/diamond_tournament_1.svg"; +import leagueDiamondTournament2Svg from "../../assets/league-tier-backgrounds/diamond_tournament_2.svg"; +import leagueDiamondTournament3Svg from "../../assets/league-tier-backgrounds/diamond_tournament_3.svg"; export const getLeagueBackgroundImage = ( - currentLeaderboardTier?: number, + currentLeaderboardTier?: Tier, useUrl = false, ): string => { - switch (Tier[currentLeaderboardTier ?? -1]) { - case "BRONZE": + switch (currentLeaderboardTier) { + case Tier.BRONZE: return useUrl ? createUrl(leagueBronzeSvg) : leagueBronzeSvg; - case "SILVER": + case Tier.SILVER: return useUrl ? createUrl(leagueSilverSvg) : leagueSilverSvg; - case "GOLD": + case Tier.GOLD: return useUrl ? createUrl(leagueGoldSvg) : leagueGoldSvg; - case "SAPPHIRE": + case Tier.SAPPHIRE: return useUrl ? createUrl(leagueSapphireSvg) : leagueSapphireSvg; - case "RUBY": + case Tier.RUBY: return useUrl ? createUrl(leagueRubySvg) : leagueRubySvg; - case "EMERALD": + case Tier.EMERALD: return useUrl ? createUrl(leagueEmeraldSvg) : leagueEmeraldSvg; - case "AMETHYST": + case Tier.AMETHYST: return useUrl ? createUrl(leagueAmethystSvg) : leagueAmethystSvg; - case "PEARL": + case Tier.PEARL: return useUrl ? createUrl(leaguePearlSvg) : leaguePearlSvg; - case "OBSIDIAN": + case Tier.OBSIDIAN: return useUrl ? createUrl(leagueObsidianSvg) : leagueObsidianSvg; - case "DIAMOND": + case Tier.DIAMOND: return useUrl ? createUrl(leagueDiamondSvg) : leagueDiamondSvg; + case Tier.DIAMOND_TOURNAMENT_1: + return useUrl + ? createUrl(leagueDiamondTournament1Svg) + : leagueDiamondTournament1Svg; + case Tier.DIAMOND_TOURNAMENT_2: + return useUrl + ? createUrl(leagueDiamondTournament2Svg) + : leagueDiamondTournament2Svg; + case Tier.DIAMOND_TOURNAMENT_3: + return useUrl + ? createUrl(leagueDiamondTournament3Svg) + : leagueDiamondTournament3Svg; default: return ""; }