Skip to content

Commit

Permalink
Use model classes instead of working directly on the responses
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanhollman committed Feb 3, 2024
1 parent ba39925 commit 067daaa
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 90 deletions.
27 changes: 5 additions & 22 deletions src/components/StreakView/User/User.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { StreakData, useUserData } from "../../../hooks/useUserData";
import { useUserData } from "../../../hooks/useUserData";
import { proxyify } from "../../../utilities";
import { FireBorder } from "./FireBorder/FireBorder";
import "./User.less";
Expand All @@ -17,39 +17,22 @@ export const User = (props: UserProps) => {
return <span>Failed to retrieve streak data...</span>;
}

const didLessonToday = didALessonToday(user.streakData);

return (
<FireBorder enabled={didLessonToday}>
<FireBorder enabled={user.streak.didALessonToday()}>
<div className="user">
<h2>{user.name}</h2>
<i>({user.courses[0]?.title})</i>
<i>({user.currentCourse?.title})</i>
<img
className="avatar"
alt={user.name}
src={proxyify(user.picture.replace("//", "https://") + "/xxlarge")}
src={proxyify(user.pictureUrl)}
/>
<StreakBlock streak={user.streak} />
<StreakBlock streak={user.streak.days} />
</div>
</FireBorder>
);
};

/**
* Checks if a lesson was done today based on the streak information.
* @param streakInfo - The streak information object.
* @returns A boolean indicating whether a lesson was done today.
*/
const didALessonToday = (streakData: StreakData): boolean => {
const streakInfo = streakData.currentStreak;
if (streakInfo) {
return (
new Date(streakInfo.endDate).getTime() >= new Date().setHours(0, 0, 0, 0)
);
}
return false;
};

type StreakBlockProps = {
streak: number;
};
Expand Down
File renamed without changes.
2 changes: 2 additions & 0 deletions src/entities/models/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./config";
export * from "./user";
75 changes: 75 additions & 0 deletions src/entities/models/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { CourseDto, StreakDataDto, UserDto } from "../responses";

export class User {
public readonly name: string;
public readonly username: string;
public readonly pictureUrl: string;
public readonly streak: Streak;
public readonly currentCourse?: CurrentCourse;

public static fromResponse(response: UserDto): User {
return new User(response);
}

private constructor(user: UserDto) {
this.name = user.name;
this.username = user.username;
this.pictureUrl = user.picture.replace("//", "https://") + "/xxlarge";
this.streak = Streak.fromResponse(user.streakData);
const currentCourse = user.courses.find(
(c) => c.id === user.currentCourseId,
);
if (currentCourse) {
this.currentCourse = CurrentCourse.fromResponse(currentCourse);
}
}
}

class Streak {
public readonly days: number = 0;
public readonly startDate?: string;
public readonly endDate?: string;

public static fromResponse(response: StreakDataDto): Streak {
if (!response.currentStreak) {
return new Streak(0);
}
return new Streak(
response.currentStreak.length,
response.currentStreak.startDate,
response.currentStreak.endDate,
);
}

private constructor(days: number, startDate?: string, endDate?: string) {
this.days = days;
this.startDate = startDate;
this.endDate = endDate;
}

/**
* Returns whether the user did a lesson today.
*/
public didALessonToday(): boolean {
if (this.endDate) {
return (
new Date(this.endDate).getTime() >= new Date().setHours(0, 0, 0, 0)
);
}
return false;
}
}

class CurrentCourse {
public readonly title: string;
public readonly languageCode: string;

public static fromResponse(response: CourseDto): CurrentCourse {
return new CurrentCourse(response);
}

private constructor(course: CourseDto) {
this.title = course.title;
this.languageCode = course.learningLanguage;
}
}
65 changes: 65 additions & 0 deletions src/entities/responses/duolingo-api-response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
export type DuolingoResponse = {
users: UserDto[];
};

export type UserDto = {
joinedClassroomIds: unknown[];
streak: number;
motivation: string;
acquisitionSurveyReason: string;
shouldForceConnectPhoneNumber: boolean;
picture: string;
learningLanguage: string;
hasFacebookId: boolean;
shakeToReportEnabled: null | boolean;
liveOpsFeatures: unknown[];
canUseModerationTools: boolean;
id: number;
betaStatus: string;
hasGoogleId: boolean;
privacySettings: string[];
fromLanguage: string;
hasRecentActivity15: boolean;
_achievements: unknown[];
observedClassroomIds: unknown[];
username: string;
bio: string;
profileCountry: null | string;
chinaUserModerationRecords: unknown[];
globalAmbassadorStatus: unknown;
currentCourseId: string;
hasPhoneNumber: boolean;
creationDate: number;
achievements: unknown[];
hasPlus: boolean;
name: string;
roles: string[];
classroomLeaderboardsEnabled: boolean;
emailVerified: boolean;
courses: CourseDto[];
totalXp: number;
streakData: StreakDataDto;
};

export type CourseDto = {
preload: boolean;
placementTestAvailable: boolean;
authorId: string;
title: string;
learningLanguage: string;
xp: number;
healthEnabled: boolean;
fromLanguage: string;
crowns: number;
id: string;
};

export type StreakDataDto = {
currentStreak: StreakInfoDto | null;
};

export type StreakInfoDto = {
startDate: string;
length: number;
endDate: string;
};
1 change: 1 addition & 0 deletions src/entities/responses/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./duolingo-api-response";
2 changes: 1 addition & 1 deletion src/hooks/useConfiguration.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useLocalStorage } from "usehooks-ts";
import { Config } from "../entities/config";
import { Config } from "../entities/models";

export function useConfiguration() {
const [config, setConfig] = useLocalStorage<Config>("config", {
Expand Down
70 changes: 3 additions & 67 deletions src/hooks/useUserData.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { useQuery } from "@tanstack/react-query";
import { fetchWithProxy } from "../utilities";
import { DuolingoResponse } from "../entities/responses";
import { User } from "../entities/models";

/**
* Custom hook to fetch the user's information from Duolingo.
Expand All @@ -23,73 +25,7 @@ export function useUserData(userName: string) {
if (isError) console.error("Error fetching streak info", data);

const isValid = !isError && data?.users?.length === 1;
const user = isValid ? data.users[0] : null;
const user = isValid ? User.fromResponse(data.users[0]) : null;

return { isPending, isValid, user };
}

type DuolingoResponse = {
users: User[];
};

export type User = {
joinedClassroomIds: unknown[];
streak: number;
motivation: string;
acquisitionSurveyReason: string;
shouldForceConnectPhoneNumber: boolean;
picture: string;
learningLanguage: string;
hasFacebookId: boolean;
shakeToReportEnabled: null | boolean;
liveOpsFeatures: unknown[];
canUseModerationTools: boolean;
id: number;
betaStatus: string;
hasGoogleId: boolean;
privacySettings: string[];
fromLanguage: string;
hasRecentActivity15: boolean;
_achievements: unknown[];
observedClassroomIds: unknown[];
username: string;
bio: string;
profileCountry: null | string;
chinaUserModerationRecords: unknown[];
globalAmbassadorStatus: unknown;
currentCourseId: string;
hasPhoneNumber: boolean;
creationDate: number;
achievements: unknown[];
hasPlus: boolean;
name: string;
roles: string[];
classroomLeaderboardsEnabled: boolean;
emailVerified: boolean;
courses: Course[];
totalXp: number;
streakData: StreakData;
};

type Course = {
preload: boolean;
placementTestAvailable: boolean;
authorId: string;
title: string;
learningLanguage: string;
xp: number;
healthEnabled: boolean;
fromLanguage: string;
crowns: number;
id: string;
};

export type StreakData = {
currentStreak: StreakInfo | null;
};

type StreakInfo = {
startDate: string;
length: number;
endDate: string;
};

0 comments on commit 067daaa

Please sign in to comment.