diff --git a/src/components/Api/Api.tsx b/src/components/Api/Api.tsx index 55b410d..54fb465 100644 --- a/src/components/Api/Api.tsx +++ b/src/components/Api/Api.tsx @@ -6,7 +6,7 @@ import { OnboardingParams, PatchStudentData, RegistrationParams, - StudentStatusParams, + StudentStatus, } from "../../interfaces/Interfaces"; export let backendURL = "https://stude.keannu1.duckdns.org"; @@ -261,20 +261,35 @@ export async function OnboardingUpdateStudentInfo(info: OnboardingParams) { }); } -export async function PostStudentStatus(info: StudentStatusParams) { +export async function GetStudentStatus() { const config = await GetConfig(); - console.log(info); return instance - .patch("/api/v1/student_status/self/", info, config) + .get("/api/v1/student_status/self/", config) .then((response) => { - console.log("heh1"); 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("heh2", error); + console.log(error_message); + return [false, error_message]; + }); +} + +export async function PatchStudentStatus(info: StudentStatus) { + const config = await GetConfig(); + console.log(info); + return instance + .patch("/api/v1/student_status/self/", info, config) + .then((response) => { + 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_message); return [false, error_message]; }); } diff --git a/src/interfaces/Interfaces.tsx b/src/interfaces/Interfaces.tsx index 76c7c8c..eb6486c 100644 --- a/src/interfaces/Interfaces.tsx +++ b/src/interfaces/Interfaces.tsx @@ -122,6 +122,23 @@ export interface PatchStudentData { avatar?: string | null; } +interface Location { + latitude: number; + longitude: number; +} + +export interface StudentStatus { + user?: string; + subject?: string; + location?: Location; + landmark?: string | null; + active?: boolean; +} + +export type StudentStatusParams = [boolean, StudentStatus]; + +export type LocationType = Location.LocationObject; + export interface StudentData { first_name: string; last_name: string; @@ -135,21 +152,8 @@ export interface StudentData { course_shortname: string; year_level: string; yearlevel_shortname: string; - subjects: string[]; // To-do + subjects: string[]; username: string; } export type UserInfoParams = [boolean, StudentData]; - -interface Location { - latitude: number; - longtitude: number; -} - -export interface StudentStatusParams { - subject?: string; - location?: Location; - active?: boolean; -} - -export type LocationType = Location.LocationObject; diff --git a/src/routes/Home/Home.tsx b/src/routes/Home/Home.tsx index 73eb82a..0b79bae 100644 --- a/src/routes/Home/Home.tsx +++ b/src/routes/Home/Home.tsx @@ -1,23 +1,34 @@ import styles, { Viewport, colors } from "../../styles"; -import { View, Text } from "react-native"; +import { View, Text, ToastAndroid } from "react-native"; import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer"; import { useState, useEffect } from "react"; import MapView, { Callout, Marker, UrlTile } from "react-native-maps"; import * as Location from "expo-location"; import GetDistance from "../../components/GetDistance/GetDistance"; import Button from "../../components/Button/Button"; -import { RootDrawerParamList } from "../../interfaces/Interfaces"; +import { + RootDrawerParamList, + StudentStatusParams, +} from "../../interfaces/Interfaces"; import { LocationType } from "../../interfaces/Interfaces"; import { useNavigation } from "@react-navigation/native"; -import { urlProvider } from "../../components/Api/Api"; +import { + GetStudentStatus, + PatchStudentStatus, + urlProvider, +} from "../../components/Api/Api"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; export default function Home() { + // Switch this condition to see the main map when debugging + const map_debug = true; const navigation = useNavigation(); const [location, setLocation] = useState(null); const [dist, setDist] = useState(null); const [feedback, setFeedback] = useState( "To continue, please allow Stud-E permission to location services" ); + const queryClient = useQueryClient(); const ustpCoords = { latitude: 8.4857, @@ -28,8 +39,10 @@ export default function Home() { async function requestLocation() { const { status } = await Location.requestForegroundPermissionsAsync(); if (status !== "granted") { - setFeedback( - "Permission to access location was denied. Please allow permission" + setFeedback("Allow location permissions to continue"); + ToastAndroid.show( + "Location permission was denied. Please allow in order to use StudE", + ToastAndroid.SHORT ); return; } @@ -71,12 +84,86 @@ export default function Home() { ustpCoords.longitude ); setDist(Math.round(dist)); + // Deactivate student status if too far away + if (dist >= 2 && !map_debug) + mutation.mutate({ + active: false, + }); } + // Student Status + const [studying, setStudying] = useState(false); + const [subject, setSubject] = useState(""); + const [buttonLabel, setButtonLabel] = useState("Start studying"); + const StudentStatus = useQuery({ + queryKey: ["user_status"], + queryFn: GetStudentStatus, + onSuccess: (data: StudentStatusParams) => { + if (data[1].active !== undefined) { + setStudying(data[1].active); + } + if (data[1].subject !== undefined) { + setSubject(data[1].subject); + } + if (data[1].active == true) { + setButtonLabel("Stop Studying"); + } else if (data[1].active == false) { + setButtonLabel("Start Studying"); + } + console.log(data[1]); + }, + onError: () => { + setFeedback("Unable to query available subjects"); + }, + }); + + const mutation = useMutation({ + mutationFn: PatchStudentStatus, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["user"] }); + queryClient.invalidateQueries({ queryKey: ["user_status"] }); + ToastAndroid.show( + "You are no longer studying " + subject, + ToastAndroid.SHORT + ); + }, + onError: () => { + ToastAndroid.show( + "Server error. Unable to update student status", + ToastAndroid.SHORT + ); + }, + }); + function CustomCallout() { + if (location && location.coords) { + if (studying) { + return ( + + + You are here {"\n"} + X: {Math.round(location.coords.longitude) + "\n"} + Z: {Math.round(location.coords.latitude) + "\n"} + Studying: {subject} + + + ); + } else { + return ( + + + You are here {"\n"} + X: {Math.round(location.coords.longitude) + "\n"} + Z: {Math.round(location.coords.latitude)} + + + ); + } + } + return <>; + } function CustomMap() { if (dist && location) { - if (dist >= 2) { - // Just switch this condition for map debugging + if (dist <= 2 || map_debug) { return ( - - - You are here {"\n"} - X: {Math.round(location.coords.longitude) + "\n"} - Z: {Math.round(location.coords.latitude)} - - + ); diff --git a/src/routes/StartStudying/StartStudying.tsx b/src/routes/StartStudying/StartStudying.tsx index 4fadf35..562762d 100644 --- a/src/routes/StartStudying/StartStudying.tsx +++ b/src/routes/StartStudying/StartStudying.tsx @@ -1,21 +1,26 @@ import * as React from "react"; import styles, { Viewport } from "../../styles"; -import { View, Text } from "react-native"; +import { View, Text, ToastAndroid } from "react-native"; import { useState } from "react"; -import { UserInfoParams, OptionType } from "../../interfaces/Interfaces"; +import { + UserInfoParams, + OptionType, + RootDrawerParamList, +} from "../../interfaces/Interfaces"; import Button from "../../components/Button/Button"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { UserInfo } from "../../components/Api/Api"; +import { PatchStudentStatus, UserInfo } from "../../components/Api/Api"; import { colors } from "../../styles"; import DropDownPicker from "react-native-dropdown-picker"; import AnimatedContainerNoScroll from "../../components/AnimatedContainer/AnimatedContainerNoScroll"; import { urlProvider } from "../../components/Api/Api"; import MapView, { UrlTile, Marker } from "react-native-maps"; +import { useNavigation } from "@react-navigation/native"; export default function StartStudying({ route }: any) { const { location } = route.params; const queryClient = useQueryClient(); - const [feedback, setFeedback] = useState(""); + const navigation = useNavigation(); // Subject choices const [selected_subject, setSelectedSubject] = useState(""); @@ -32,9 +37,32 @@ export default function StartStudying({ route }: any) { setSubjects(subjects); }, onError: () => { - setFeedback("Unable to query available subjects"); + ToastAndroid.show( + "Server error: Unable to query available subjects", + ToastAndroid.SHORT + ); }, }); + + const mutation = useMutation({ + mutationFn: PatchStudentStatus, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["user"] }); + queryClient.invalidateQueries({ queryKey: ["user_status"] }); + ToastAndroid.show( + "You are now studying " + selected_subject, + ToastAndroid.SHORT + ); + navigation.navigate("Home"); + }, + onError: () => { + ToastAndroid.show( + "A server error has occured. Please try again", + ToastAndroid.SHORT + ); + }, + }); + if (location && location.coords) { return ( @@ -87,50 +115,60 @@ export default function StartStudying({ route }: any) { /> - - {feedback} - - - - Start Studying - - - { - setSubjectsOpen(open); - }} - setValue={setSelectedSubject} - placeholderStyle={{ - ...styles.text_white_tiny_bold, - ...{ textAlign: "left" }, - }} - placeholder="Select subject" - multipleText="Select subject" - style={styles.input} - textStyle={{ - ...styles.text_white_tiny_bold, - ...{ textAlign: "left" }, - }} - modalContentContainerStyle={{ - backgroundColor: colors.primary_2, - borderWidth: 0, - zIndex: 1000, - }} - autoScroll - dropDownDirection="BOTTOM" - listMode="MODAL" - /> - + { + setSubjectsOpen(open); + }} + setValue={setSelectedSubject} + placeholderStyle={{ + ...styles.text_white_tiny_bold, + ...{ textAlign: "left" }, + }} + placeholder="Select subject" + multipleText="Select subject" + style={styles.input} + textStyle={{ + ...styles.text_white_tiny_bold, + ...{ textAlign: "left" }, + }} + modalContentContainerStyle={{ + backgroundColor: colors.primary_2, + borderWidth: 0, + zIndex: 1000, + }} + autoScroll + dropDownDirection="BOTTOM" + listMode="MODAL" + /> - + ); diff --git a/src/routes/SubjectsPage/SubjectsPage.tsx b/src/routes/SubjectsPage/SubjectsPage.tsx index b0429e0..2db5e3b 100644 --- a/src/routes/SubjectsPage/SubjectsPage.tsx +++ b/src/routes/SubjectsPage/SubjectsPage.tsx @@ -52,7 +52,7 @@ export default function SubjectsPage() { course_shortname: "", avatar: "", student_id_number: "", - subjects: [] as Subjects, + subjects: [] as string[], }); const StudentInfo = useQuery({ queryKey: ["user"], diff --git a/src/routes/UserInfoPage/UserInfoPage.tsx b/src/routes/UserInfoPage/UserInfoPage.tsx index d713a2f..63d1b39 100644 --- a/src/routes/UserInfoPage/UserInfoPage.tsx +++ b/src/routes/UserInfoPage/UserInfoPage.tsx @@ -28,6 +28,7 @@ import { GetSemesters, GetSubjects, GetYearLevels, + PatchStudentStatus, PatchUserInfo, UserInfo, } from "../../components/Api/Api"; @@ -47,6 +48,20 @@ export default function UserInfoPage() { const dispatch = useDispatch(); const queryClient = useQueryClient(); const [feedback, setFeedback] = useState(""); + + // Student Status + const studentstatus_mutation = useMutation({ + mutationFn: PatchStudentStatus, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["user"] }); + queryClient.invalidateQueries({ queryKey: ["user_status"] }); + setFeedback("Changes applied successfully"); + }, + onError: () => { + setFeedback("An error has occured\nChanges have not been saved"); + }, + }); + // User Info const [user, setUser] = useState({ first_name: "", @@ -86,11 +101,16 @@ export default function UserInfoPage() { setFeedback("Unable to query user info"); }, }); + const mutation = useMutation({ mutationFn: PatchUserInfo, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["user"] }); queryClient.invalidateQueries({ queryKey: ["subjects"] }); + // Reset student status when changing user info to prevent bugs + studentstatus_mutation.mutate({ + active: false, + }); setFeedback("Changes applied successfully"); dispatch(setUserinState(user)); },