From 497e50f2a4d27bab091f3e38f9f6769a84c909fd Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Mon, 14 Aug 2023 23:29:53 +0800 Subject: [PATCH] Refactored types for better readability --- src/components/Api/Api.tsx | 45 +++++----------- src/interfaces/Interfaces.tsx | 60 +++++++++++----------- src/routes/Home/Home.tsx | 23 +++++++-- src/routes/Onboarding/Onboarding.tsx | 17 +++--- src/routes/Register/Register.tsx | 1 - src/routes/Revalidation/Revalidation.tsx | 4 +- src/routes/StartStudying/StartStudying.tsx | 17 ++++-- src/routes/SubjectsPage/SubjectsPage.tsx | 52 +++++++++++++++---- src/routes/UserInfoPage/UserInfoPage.tsx | 55 +++++++++++--------- 9 files changed, 157 insertions(+), 117 deletions(-) diff --git a/src/components/Api/Api.tsx b/src/components/Api/Api.tsx index 4564ee4..8524c32 100644 --- a/src/components/Api/Api.tsx +++ b/src/components/Api/Api.tsx @@ -1,12 +1,12 @@ import axios from "axios"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { - ActivationParams, - LoginParams, - OnboardingParams, - PatchStudentData, - RegistrationParams, - StudentStatus, + ActivationType, + LoginType, + OnboardingType, + PatchUserInfoType, + RegistrationType, + StudentStatusType, } from "../../interfaces/Interfaces"; export let backendURL = "https://stude.keannu1.duckdns.org"; @@ -67,7 +67,7 @@ export async function GetConfig() { } // User APIs -export function UserRegister(register: RegistrationParams) { +export function UserRegister(register: RegistrationType) { console.log(JSON.stringify(register)); return instance .post("/api/v1/accounts/users/", register) @@ -82,7 +82,7 @@ export function UserRegister(register: RegistrationParams) { }); } -export function UserLogin(user: LoginParams) { +export function UserLogin(user: LoginType) { return instance .post("/api/v1/accounts/jwt/create/", user) .then(async (response) => { @@ -128,7 +128,7 @@ export async function TokenRefresh() { return false; }); } -export async function UserInfo() { +export async function GetUserInfo() { const config = await GetConfig(); return instance .get("/api/v1/accounts/users/me/", config) @@ -144,7 +144,7 @@ export async function UserInfo() { }); } -export async function PatchUserInfo(info: PatchStudentData) { +export async function PatchUserInfo(info: PatchUserInfoType) { const config = await GetConfig(); return instance .patch("/api/v1/accounts/users/me/", info, config) @@ -160,13 +160,13 @@ export async function PatchUserInfo(info: PatchStudentData) { }); } -export function UserActivate(activation: ActivationParams) { +export function UserActivate(activation: ActivationType) { return instance .post("/api/v1/accounts/users/activation/", activation) - .then(async (response) => { + .then(() => { return true; }) - .catch((error) => { + .catch(() => { return false; }); } @@ -244,23 +244,6 @@ export async function GetSubjects() { }); } -export async function OnboardingUpdateStudentInfo(info: OnboardingParams) { - const config = await GetConfig(); - return instance - .patch("/api/v1/accounts/users/me/", info, config) - .then((response) => { - console.log(JSON.stringify(response.data)); - return [true, response.data]; - }) - .catch((error) => { - let error_message = ""; - if (error.response) error_message = error.response.data; - else error_message = "Unable to reach servers"; - console.log("Error updating onboarding info", error_message); - return [false, error_message]; - }); -} - export async function GetStudentStatus() { const config = await GetConfig(); return instance @@ -277,7 +260,7 @@ export async function GetStudentStatus() { }); } -export async function PatchStudentStatus(info: StudentStatus) { +export async function PatchStudentStatus(info: StudentStatusType) { const config = await GetConfig(); console.log(info); return instance diff --git a/src/interfaces/Interfaces.tsx b/src/interfaces/Interfaces.tsx index eb6486c..85ef9ba 100644 --- a/src/interfaces/Interfaces.tsx +++ b/src/interfaces/Interfaces.tsx @@ -30,7 +30,7 @@ export interface LoggedInUserState { // API Interfaces -export interface RegistrationParams { +export interface RegistrationType { email: string; username: string; password: string; @@ -39,12 +39,12 @@ export interface RegistrationParams { student_id_number: string; } -export interface LoginParams { +export interface LoginType { username: string; password: string; } -export interface ActivationParams { +export interface ActivationType { uid: string; token: string; } @@ -55,38 +55,38 @@ export interface OptionType { } // Semester -export interface Semester { +export interface SemesterType { id: string; name: string; shortname: string; } -export type Semesters = Array; +export type SemestersType = Array; -export type SemesterParams = [boolean, Semesters]; +export type SemesterReturnType = [boolean, SemestersType]; // Year Level -export interface YearLevel { +export interface YearLevelType { id: string; name: string; shortname: string; } -export type YearLevels = Array; +export type YearLevelsType = Array; -export type YearLevelParams = [boolean, YearLevels]; +export type YearLevelReturnType = [boolean, YearLevelsType]; // Course -export interface Course { +export interface CourseType { id: string; name: string; shortname: string; } -export type Courses = Array; -export type CourseParams = [boolean, Courses]; +export type CoursesType = Array; +export type CourseReturnType = [boolean, CoursesType]; // Subject -export interface Subject { +export interface SubjectType { id: number; name: string; code: string; @@ -95,31 +95,31 @@ export interface Subject { semester: string; } -export type Subjects = Array; -export type SubjectParams = [boolean, Subjects]; +export type SubjectsType = Array; +export type SubjectsReturnType = [boolean, SubjectsType]; -export type avatar = { +export type AvatarType = { uri: string; type: string; name: string; }; // For dropdown menu -export interface OnboardingParams { +export interface OnboardingType { year_level: string; course: string; semester: string; } -export interface PatchStudentData { - course?: string | null; - first_name?: string | null; - last_name?: string | null; - semester?: string | null; - subjects?: any[] | null; // To-do, replace 'any' with your actual type - year_level?: string | null; - irregular?: boolean | null; - avatar?: string | null; +export interface PatchUserInfoType { + course?: string; + first_name?: string; + last_name?: string; + semester?: string; + subjects?: string[]; + year_level?: string; + irregular?: boolean; + avatar?: string; } interface Location { @@ -127,7 +127,7 @@ interface Location { longitude: number; } -export interface StudentStatus { +export interface StudentStatusType { user?: string; subject?: string; location?: Location; @@ -135,11 +135,11 @@ export interface StudentStatus { active?: boolean; } -export type StudentStatusParams = [boolean, StudentStatus]; +export type StudentStatusReturnType = [boolean, StudentStatusType]; export type LocationType = Location.LocationObject; -export interface StudentData { +export interface UserInfoType { first_name: string; last_name: string; email: string; @@ -156,4 +156,4 @@ export interface StudentData { username: string; } -export type UserInfoParams = [boolean, StudentData]; +export type UserInfoReturnType = [boolean, UserInfoType]; diff --git a/src/routes/Home/Home.tsx b/src/routes/Home/Home.tsx index 759eef4..58e1509 100644 --- a/src/routes/Home/Home.tsx +++ b/src/routes/Home/Home.tsx @@ -8,9 +8,10 @@ import GetDistance from "../../components/GetDistance/GetDistance"; import Button from "../../components/Button/Button"; import { RootDrawerParamList, - StudentStatusParams, + StudentStatusReturnType, + LocationType, + StudentStatusType, } from "../../interfaces/Interfaces"; -import { LocationType } from "../../interfaces/Interfaces"; import { useNavigation } from "@react-navigation/native"; import { GetStudentStatus, @@ -104,8 +105,14 @@ export default function Home() { const [buttonLabel, setButtonLabel] = useState("Start studying"); const StudentStatus = useQuery({ queryKey: ["user_status"], - queryFn: GetStudentStatus, - onSuccess: (data: StudentStatusParams) => { + queryFn: async () => { + const data = await GetStudentStatus(); + if (data[0] == false) { + return Promise.reject(new Error()); + } + return data; + }, + onSuccess: (data: StudentStatusReturnType) => { if (data[1].active !== undefined) { setStudying(data[1].active); } @@ -125,7 +132,13 @@ export default function Home() { }); const mutation = useMutation({ - mutationFn: PatchStudentStatus, + mutationFn: async (info: StudentStatusType) => { + const data = await PatchStudentStatus(info); + if (data[0] != true) { + return Promise.reject(new Error()); + } + return data; + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["user"] }); queryClient.invalidateQueries({ queryKey: ["user_status"] }); diff --git a/src/routes/Onboarding/Onboarding.tsx b/src/routes/Onboarding/Onboarding.tsx index 0d52063..5e9ea3a 100644 --- a/src/routes/Onboarding/Onboarding.tsx +++ b/src/routes/Onboarding/Onboarding.tsx @@ -3,10 +3,10 @@ import styles from "../../styles"; import { View, Text } from "react-native"; import { useNavigation } from "@react-navigation/native"; import { - Course, RootDrawerParamList, - Semester, - YearLevel, + CourseType, + SemesterType, + YearLevelType, } from "../../interfaces/Interfaces"; import { colors } from "../../styles"; import { MotiView } from "moti"; @@ -18,12 +18,11 @@ import { GetCourses, GetSemesters, GetYearLevels, - OnboardingUpdateStudentInfo, + PatchUserInfo, } from "../../components/Api/Api"; import { useDispatch } from "react-redux"; import { unsetOnboarding } from "../../features/redux/slices/StatusSlice/StatusSlice"; import { setUser } from "../../features/redux/slices/UserSlice/UserSlice"; -import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer"; import AnimatedContainerNoScroll from "../../components/AnimatedContainer/AnimatedContainerNoScroll"; import { useToast } from "react-native-toast-notifications"; export default function Onboarding() { @@ -42,7 +41,7 @@ export default function Onboarding() { queryKey: ["semesters"], queryFn: GetSemesters, onSuccess: (data) => { - let semesters = data[1].map((item: Semester) => ({ + let semesters = data[1].map((item: SemesterType) => ({ label: item.name, value: item.name, })); @@ -68,7 +67,7 @@ export default function Onboarding() { queryKey: ["year_levels"], queryFn: GetYearLevels, onSuccess: (data) => { - let year_levels = data[1].map((item: YearLevel) => ({ + let year_levels = data[1].map((item: YearLevelType) => ({ label: item.name, value: item.name, })); @@ -97,7 +96,7 @@ export default function Onboarding() { queryKey: ["courses"], queryFn: GetCourses, onSuccess: (data) => { - let courses = data[1].map((item: Course) => ({ + let courses = data[1].map((item: CourseType) => ({ label: item.name, value: item.name, })); @@ -241,7 +240,7 @@ export default function Onboarding() { !selected_yearlevel || !selected_course || !selected_semester } onPress={async () => { - let result = await OnboardingUpdateStudentInfo({ + let result = await PatchUserInfo({ semester: selected_semester, course: selected_course, year_level: selected_yearlevel, diff --git a/src/routes/Register/Register.tsx b/src/routes/Register/Register.tsx index 37f3a0f..09633a5 100644 --- a/src/routes/Register/Register.tsx +++ b/src/routes/Register/Register.tsx @@ -15,7 +15,6 @@ import { RootDrawerParamList } from "../../interfaces/Interfaces"; import SignupIcon from "../../icons/SignupIcon/SignupIcon"; import { UserRegister } from "../../components/Api/Api"; import IsNumber from "../../components/IsNumber/IsNumber"; -import ParseError from "../../components/ParseError/ParseError"; import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer"; import { useToast } from "react-native-toast-notifications"; diff --git a/src/routes/Revalidation/Revalidation.tsx b/src/routes/Revalidation/Revalidation.tsx index c633f99..c583a1d 100644 --- a/src/routes/Revalidation/Revalidation.tsx +++ b/src/routes/Revalidation/Revalidation.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import styles from "../../styles"; import { View, Text, ActivityIndicator } from "react-native"; -import { TokenRefresh, UserInfo } from "../../components/Api/Api"; +import { TokenRefresh, GetUserInfo } from "../../components/Api/Api"; import { useDispatch } from "react-redux"; import { colors } from "../../styles"; import { useEffect, useState } from "react"; @@ -25,7 +25,7 @@ export default function Revalidation() { useEffect(() => { setState("Previous session found"); TokenRefresh().then(async (response) => { - let user_info = await UserInfo(); + let user_info = await GetUserInfo(); if (response && user_info[0]) { dispatch(login()); dispatch(setUser(user_info[1])); diff --git a/src/routes/StartStudying/StartStudying.tsx b/src/routes/StartStudying/StartStudying.tsx index fb46b29..cea7bb9 100644 --- a/src/routes/StartStudying/StartStudying.tsx +++ b/src/routes/StartStudying/StartStudying.tsx @@ -3,13 +3,14 @@ import styles, { Viewport } from "../../styles"; import { View, Text, ToastAndroid } from "react-native"; import { useState } from "react"; import { - UserInfoParams, + UserInfoReturnType, OptionType, RootDrawerParamList, + StudentStatusType, } from "../../interfaces/Interfaces"; import Button from "../../components/Button/Button"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { PatchStudentStatus, UserInfo } from "../../components/Api/Api"; +import { PatchStudentStatus, GetUserInfo } from "../../components/Api/Api"; import { colors } from "../../styles"; import DropDownPicker from "react-native-dropdown-picker"; import AnimatedContainerNoScroll from "../../components/AnimatedContainer/AnimatedContainerNoScroll"; @@ -30,8 +31,8 @@ export default function StartStudying({ route }: any) { const [subjects, setSubjects] = useState([]); const StudentInfo = useQuery({ queryKey: ["user"], - queryFn: UserInfo, - onSuccess: (data: UserInfoParams) => { + queryFn: GetUserInfo, + onSuccess: (data: UserInfoReturnType) => { let subjects = data[1].subjects.map((subject: string) => ({ label: subject, value: subject, @@ -49,7 +50,13 @@ export default function StartStudying({ route }: any) { }); const mutation = useMutation({ - mutationFn: PatchStudentStatus, + mutationFn: async (info: StudentStatusType) => { + const data = await PatchStudentStatus(info); + if (data[0] == false) { + return Promise.reject(new Error("Error updating student status")); + } + return data; + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["user"] }); queryClient.invalidateQueries({ queryKey: ["user_status"] }); diff --git a/src/routes/SubjectsPage/SubjectsPage.tsx b/src/routes/SubjectsPage/SubjectsPage.tsx index 68b5bd5..a43dc33 100644 --- a/src/routes/SubjectsPage/SubjectsPage.tsx +++ b/src/routes/SubjectsPage/SubjectsPage.tsx @@ -3,15 +3,21 @@ import styles from "../../styles"; import { View, Text } from "react-native"; import { useState } from "react"; import { - UserInfoParams, - SubjectParams, - Subject, + UserInfoReturnType, + SubjectsReturnType, + SubjectType, OptionType, + StudentStatusType, } from "../../interfaces/Interfaces"; import Button from "../../components/Button/Button"; import { Image } from "react-native"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { GetSubjects, PatchUserInfo, UserInfo } from "../../components/Api/Api"; +import { + GetSubjects, + PatchUserInfo, + GetUserInfo, + PatchStudentStatus, +} from "../../components/Api/Api"; import { colors } from "../../styles"; import DropDownPicker from "react-native-dropdown-picker"; import AnimatedContainerNoScroll from "../../components/AnimatedContainer/AnimatedContainerNoScroll"; @@ -23,6 +29,30 @@ export default function SubjectsPage() { const logged_in_user = useSelector((state: RootState) => state.user.user); const queryClient = useQueryClient(); const toast = useToast(); + + // Student Status + const studentstatus_mutation = useMutation({ + mutationFn: async (info: StudentStatusType) => { + const data = await PatchStudentStatus(info); + if (data[0] != true) { + return Promise.reject(new Error()); + } + return data; + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["user"] }); + queryClient.invalidateQueries({ queryKey: ["user_status"] }); + }, + onError: () => { + toast.show("An error has occured\nChanges have not been saved", { + type: "warning", + placement: "top", + duration: 2000, + animationType: "slide-in", + }); + }, + }); + // User Info const [user, setUser] = useState({ first_name: "", @@ -39,8 +69,8 @@ export default function SubjectsPage() { }); const StudentInfo = useQuery({ queryKey: ["user"], - queryFn: UserInfo, - onSuccess: (data: UserInfoParams) => { + queryFn: GetUserInfo, + onSuccess: (data: UserInfoReturnType) => { setUser({ ...user, first_name: data[1].first_name, @@ -69,7 +99,11 @@ export default function SubjectsPage() { queryClient.invalidateQueries({ queryKey: ["user"] }); queryClient.invalidateQueries({ queryKey: ["subjects"] }); setSelectedSubjects([]); - toast.show("Changes applied successfully", { + // Reset student status when changing user info to prevent bugs + studentstatus_mutation.mutate({ + active: false, + }); + toast.show("Changes applied successfully.\nStudent status reset", { type: "success", placement: "top", duration: 2000, @@ -88,9 +122,9 @@ export default function SubjectsPage() { enabled: StudentInfo.isFetched, queryKey: ["subjects"], queryFn: GetSubjects, - onSuccess: (data: SubjectParams) => { + onSuccess: (data: SubjectsReturnType) => { if (data[1]) { - let subjects = data[1].map((subject: Subject) => ({ + let subjects = data[1].map((subject: SubjectType) => ({ label: subject.name, value: subject.name, })); diff --git a/src/routes/UserInfoPage/UserInfoPage.tsx b/src/routes/UserInfoPage/UserInfoPage.tsx index 3836bf7..1bd0d34 100644 --- a/src/routes/UserInfoPage/UserInfoPage.tsx +++ b/src/routes/UserInfoPage/UserInfoPage.tsx @@ -10,15 +10,15 @@ import { } from "react-native"; import { useState } from "react"; import { - SemesterParams, - UserInfoParams, - Semester, - SubjectParams, - Subject, - YearLevel, - Course, + SemesterReturnType, + UserInfoReturnType, + SemesterType, + YearLevelType, + CourseType, OptionType, - Subjects, + StudentStatusType, + UserInfoType, + PatchUserInfoType, } from "../../interfaces/Interfaces"; import Button from "../../components/Button/Button"; import { Image } from "react-native"; @@ -26,11 +26,10 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { GetCourses, GetSemesters, - GetSubjects, GetYearLevels, PatchStudentStatus, PatchUserInfo, - UserInfo, + GetUserInfo, } from "../../components/Api/Api"; import { colors } from "../../styles"; import DropDownPicker from "react-native-dropdown-picker"; @@ -52,16 +51,16 @@ export default function UserInfoPage() { // Student Status const studentstatus_mutation = useMutation({ - mutationFn: PatchStudentStatus, + mutationFn: async (info: StudentStatusType) => { + const data = await PatchStudentStatus(info); + if (data[0] != true) { + return Promise.reject(new Error()); + } + return data; + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["user"] }); queryClient.invalidateQueries({ queryKey: ["user_status"] }); - toast.show("Student Status has been reset", { - type: "success", - placement: "top", - duration: 2000, - animationType: "slide-in", - }); }, onError: () => { toast.show("An error has occured\nChanges have not been saved", { @@ -89,8 +88,8 @@ export default function UserInfoPage() { }); const StudentInfo = useQuery({ queryKey: ["user"], - queryFn: UserInfo, - onSuccess: (data: UserInfoParams) => { + queryFn: GetUserInfo, + onSuccess: (data: UserInfoReturnType) => { // console.log(data[1]); setUser({ ...user, @@ -119,7 +118,13 @@ export default function UserInfoPage() { }); const mutation = useMutation({ - mutationFn: PatchUserInfo, + mutationFn: async (info: PatchUserInfoType) => { + const data = await PatchUserInfo(info); + if (data[0] == false) { + return Promise.reject(new Error()); + } + return data; + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["user"] }); queryClient.invalidateQueries({ queryKey: ["subjects"] }); @@ -127,7 +132,7 @@ export default function UserInfoPage() { studentstatus_mutation.mutate({ active: false, }); - toast.show("Changes applied successfully", { + toast.show("Changes applied successfully.\nStudent status reset", { type: "success", placement: "top", duration: 2000, @@ -152,8 +157,8 @@ export default function UserInfoPage() { const Semesters = useQuery({ queryKey: ["semesters"], queryFn: GetSemesters, - onSuccess: (data: SemesterParams) => { - let semestersData = data[1].map((semester: Semester) => ({ + onSuccess: (data: SemesterReturnType) => { + let semestersData = data[1].map((semester: SemesterType) => ({ label: semester.name, value: semester.name, shortname: semester.shortname, @@ -179,7 +184,7 @@ export default function UserInfoPage() { queryKey: ["year_levels"], queryFn: GetYearLevels, onSuccess: (data) => { - let year_levels = data[1].map((yearlevel: YearLevel) => ({ + let year_levels = data[1].map((yearlevel: YearLevelType) => ({ label: yearlevel.name, value: yearlevel.name, })); @@ -203,7 +208,7 @@ export default function UserInfoPage() { queryKey: ["courses"], queryFn: GetCourses, onSuccess: (data) => { - let courses = data[1].map((course: Course) => ({ + let courses = data[1].map((course: CourseType) => ({ label: course.name, value: course.name, }));