mirror of
https://github.com/lemeow125/StudE-Frontend.git
synced 2025-05-17 03:48:06 +08:00
Merge branch 'master' into initial-frontend
This commit is contained in:
commit
fab7491a8d
25 changed files with 2331 additions and 524 deletions
|
@ -1,21 +1,28 @@
|
|||
import axios from "axios";
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
import {
|
||||
ActivationParams,
|
||||
LoginParams,
|
||||
OnboardingParams,
|
||||
PatchStudentData,
|
||||
RegistrationParams,
|
||||
StudentData,
|
||||
ActivationType,
|
||||
LocationType,
|
||||
LoginType,
|
||||
OnboardingType,
|
||||
PatchUserInfoType,
|
||||
RegistrationType,
|
||||
StudentStatusPatchType,
|
||||
StudentStatusType,
|
||||
StudyGroupCreateType,
|
||||
StudyGroupType,
|
||||
} from "../../interfaces/Interfaces";
|
||||
|
||||
export let backendURL = "";
|
||||
export let backendURLWebsocket = "";
|
||||
let use_production = true;
|
||||
if (__DEV__ || !use_production) {
|
||||
backendURL = "http://10.0.10.8:8000";
|
||||
backendURLWebsocket = "ws://10.0.10.8:8000";
|
||||
} else {
|
||||
export let backendURL = "https://stude.keannu1.duckdns.org";
|
||||
export let backendURLWebsocket = "ws://stude.keannu1.duckdns.org";
|
||||
if (__DEV__) {
|
||||
backendURL = "http://10.0.10.8:8083";
|
||||
backendURLWebsocket = "ws://10.0.10.8:8083";
|
||||
}
|
||||
|
||||
// Switch this on if you wanna run production URLs while in development
|
||||
let use_production = false;
|
||||
if (__DEV__ && use_production) {
|
||||
backendURL = "https://stude.keannu1.duckdns.org";
|
||||
backendURLWebsocket = "ws://stude.keannu1.duckdns.org";
|
||||
}
|
||||
|
@ -25,8 +32,29 @@ const instance = axios.create({
|
|||
timeout: 1000,
|
||||
});
|
||||
|
||||
console.log("Using backend API:", backendURL);
|
||||
|
||||
// 3rd Party APIs
|
||||
export const urlProvider =
|
||||
"https://openstreetmap.keannu1.duckdns.org/tile/{z}/{x}/{y}.png?";
|
||||
// App APIs
|
||||
|
||||
// Error Handling
|
||||
export function ParseError(error: any) {
|
||||
if (error.response && error.response.data) {
|
||||
return JSON.stringify(error.response.data)
|
||||
.replaceAll(/[{}()"]/g, " ")
|
||||
.replaceAll(/,/g, "\n")
|
||||
.replaceAll("[", "")
|
||||
.replaceAll("]", "")
|
||||
.replaceAll(".", "")
|
||||
.replaceAll(/"/g, "")
|
||||
.replaceAll("non_field_errors", "")
|
||||
.trim();
|
||||
}
|
||||
return "Unable to reach server";
|
||||
}
|
||||
|
||||
// Token Handling
|
||||
export async function getAccessToken() {
|
||||
const accessToken = await AsyncStorage.getItem("access_token");
|
||||
|
@ -59,40 +87,28 @@ export async function GetConfig() {
|
|||
}
|
||||
|
||||
// User APIs
|
||||
export function UserRegister(register: RegistrationParams) {
|
||||
console.log(JSON.stringify(register));
|
||||
export function UserRegister(register: RegistrationType) {
|
||||
return instance
|
||||
.post("/api/v1/accounts/users/", register)
|
||||
.then(async (response) => {
|
||||
return [true, response.status];
|
||||
})
|
||||
.catch((error) => {
|
||||
let error_message = "";
|
||||
if (error.response) error_message = error.response.data;
|
||||
else error_message = "Unable to reach servers";
|
||||
let error_message = ParseError(error);
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
|
||||
export function UserLogin(user: LoginParams) {
|
||||
export function UserLogin(user: LoginType) {
|
||||
return instance
|
||||
.post("/api/v1/accounts/jwt/create/", user)
|
||||
.then(async (response) => {
|
||||
/*console.log(
|
||||
"Access Token:",
|
||||
response.data.access,
|
||||
"\nRefresh Token:",
|
||||
response.data.refresh
|
||||
);*/
|
||||
setAccessToken(response.data.access);
|
||||
setRefreshToken(response.data.refresh);
|
||||
return [true];
|
||||
})
|
||||
.catch((error) => {
|
||||
let error_message = "";
|
||||
if (error.response) error_message = error.response.data;
|
||||
else error_message = "Unable to reach servers";
|
||||
// console.log(error_message);
|
||||
let error_message = ParseError(error);
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
|
@ -106,21 +122,14 @@ export async function TokenRefresh() {
|
|||
})
|
||||
.then(async (response) => {
|
||||
setAccessToken(response.data.access);
|
||||
/*console.log(
|
||||
"Token refresh success! New Access Token",
|
||||
response.data.access
|
||||
);*/
|
||||
return true;
|
||||
})
|
||||
.catch((error) => {
|
||||
let error_message = "";
|
||||
if (error.response) error_message = error.response.data;
|
||||
else error_message = "Unable to reach servers";
|
||||
console.log("Token Refresh error:", error_message);
|
||||
let error_message = ParseError(error);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
export async function UserInfo() {
|
||||
export async function GetUserInfo() {
|
||||
const config = await GetConfig();
|
||||
return instance
|
||||
.get("/api/v1/accounts/users/me/", config)
|
||||
|
@ -129,37 +138,31 @@ export async function UserInfo() {
|
|||
return [true, response.data];
|
||||
})
|
||||
.catch((error) => {
|
||||
let error_message = "";
|
||||
if (error.response) error_message = error.response.data;
|
||||
else error_message = "Unable to reach servers";
|
||||
let error_message = ParseError(error);
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
|
||||
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)
|
||||
.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_message);
|
||||
let error_message = ParseError(error);
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
@ -179,9 +182,7 @@ export async function GetCourses() {
|
|||
return [true, response.data];
|
||||
})
|
||||
.catch((error) => {
|
||||
let error_message = "";
|
||||
if (error.response) error_message = error.response.data;
|
||||
else error_message = "Unable to reach servers";
|
||||
let error_message = ParseError(error);
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
|
@ -199,9 +200,7 @@ export async function GetSemesters() {
|
|||
return [true, response.data];
|
||||
})
|
||||
.catch((error) => {
|
||||
let error_message = "";
|
||||
if (error.response) error_message = error.response.data;
|
||||
else error_message = "Unable to reach servers";
|
||||
let error_message = ParseError(error);
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
|
@ -215,69 +214,135 @@ export async function GetYearLevels() {
|
|||
return [true, response.data];
|
||||
})
|
||||
.catch((error) => {
|
||||
let error_message = "";
|
||||
if (error.response) error_message = error.response.data;
|
||||
else error_message = "Unable to reach servers";
|
||||
let error_message = ParseError(error);
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
|
||||
export async function GetSubjects(
|
||||
byCourseOnly: boolean,
|
||||
course: string,
|
||||
year_level?: string,
|
||||
semester?: string
|
||||
) {
|
||||
const config = await GetConfig();
|
||||
console.log("by course only?", byCourseOnly);
|
||||
// If year level and semester specified,
|
||||
if (!byCourseOnly && year_level && semester) {
|
||||
return instance
|
||||
.get(
|
||||
"/api/v1/subjects/" + course + "/" + year_level + "/" + semester,
|
||||
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";
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
// If only course is specified
|
||||
else {
|
||||
return instance
|
||||
.get("/api/v1/subjects/" + course, 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";
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function OnboardingUpdateStudentInfo(info: OnboardingParams) {
|
||||
export async function GetSubjects() {
|
||||
const config = await GetConfig();
|
||||
return instance
|
||||
.patch("/api/v1/accounts/users/me/", info, config)
|
||||
.get("/api/v1/subjects/", 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);
|
||||
let error_message = ParseError(error);
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
|
||||
export async function GetStudentStatus() {
|
||||
const config = await GetConfig();
|
||||
return instance
|
||||
.get("/api/v1/student_status/self/", config)
|
||||
.then((response) => {
|
||||
return [true, response.data];
|
||||
})
|
||||
.catch((error) => {
|
||||
let error_message = ParseError(error);
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
|
||||
export async function PatchStudentStatus(info: StudentStatusPatchType) {
|
||||
const config = await GetConfig();
|
||||
return instance
|
||||
.patch("/api/v1/student_status/self/", info, config)
|
||||
.then((response) => {
|
||||
return [true, response.data];
|
||||
})
|
||||
.catch((error) => {
|
||||
let error_message = ParseError(error);
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
|
||||
export async function GetStudentStatusList() {
|
||||
const config = await GetConfig();
|
||||
return instance
|
||||
.get("/api/v1/student_status/list/", config)
|
||||
.then((response) => {
|
||||
return [true, response.data];
|
||||
})
|
||||
.catch((error) => {
|
||||
let error_message = ParseError(error);
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
|
||||
export async function GetStudentStatusListNear() {
|
||||
const config = await GetConfig();
|
||||
return instance
|
||||
.get("/api/v1/student_status/near/", config)
|
||||
.then((response) => {
|
||||
return [true, response.data];
|
||||
})
|
||||
.catch((error) => {
|
||||
let error_message = ParseError(error);
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
|
||||
// To-do
|
||||
export async function GetStudentStatusListFilteredCurrentLocation(
|
||||
location: LocationType
|
||||
) {
|
||||
const config = await GetConfig();
|
||||
return instance
|
||||
.post(
|
||||
"/api/v1/student_status/near_current_location/",
|
||||
{
|
||||
location: location,
|
||||
},
|
||||
config
|
||||
)
|
||||
.then((response) => {
|
||||
return [true, response.data];
|
||||
})
|
||||
.catch((error) => {
|
||||
let error_message = ParseError(error);
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
|
||||
export async function GetStudyGroupListFiltered() {
|
||||
const config = await GetConfig();
|
||||
return instance
|
||||
.get("/api/v1/study_groups/near/", config)
|
||||
.then((response) => {
|
||||
console.log("DEBUGGG", response.data);
|
||||
return [true, response.data];
|
||||
})
|
||||
.catch((error) => {
|
||||
let error_message = ParseError(error);
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
|
||||
export async function GetStudyGroupList() {
|
||||
const config = await GetConfig();
|
||||
return instance
|
||||
.get("/api/v1/study_groups/", config)
|
||||
.then((response) => {
|
||||
return [true, response.data];
|
||||
})
|
||||
.catch((error) => {
|
||||
let error_message = ParseError(error);
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
|
||||
export async function CreateStudyGroup(info: StudyGroupCreateType) {
|
||||
const config = await GetConfig();
|
||||
// console.log("Creating study group:", info);
|
||||
return instance
|
||||
.post("/api/v1/study_groups/create/", info, config)
|
||||
.then((response) => {
|
||||
return [true, response.data];
|
||||
})
|
||||
.catch((error) => {
|
||||
let error_message = ParseError(error);
|
||||
return [false, error_message];
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,7 +5,10 @@ import { Text, View } from "react-native";
|
|||
import { colors } from "../../styles";
|
||||
import styles from "../../styles";
|
||||
|
||||
import { RootDrawerParamList } from "../../interfaces/Interfaces";
|
||||
import {
|
||||
RootDrawerParamList,
|
||||
StudentStatusPatchType,
|
||||
} from "../../interfaces/Interfaces";
|
||||
import AppIcon from "../../icons/AppIcon/AppIcon";
|
||||
import HomeIcon from "../../icons/HomeIcon/HomeIcon";
|
||||
import LoginIcon from "../../icons/LoginIcon/LoginIcon";
|
||||
|
@ -18,11 +21,48 @@ import { logout } from "../../features/redux/slices/StatusSlice/StatusSlice";
|
|||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
import UserIcon from "../../icons/UserIcon/UserIcon";
|
||||
import SubjectIcon from "../../icons/SubjectIcon/SubjectIcon";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import toast from "react-native-toast-notifications/lib/typescript/toast";
|
||||
import { PatchStudentStatus } from "../Api/Api";
|
||||
import { useToast } from "react-native-toast-notifications";
|
||||
|
||||
export default function CustomDrawerContent(props: {}) {
|
||||
const navigation = useNavigation<RootDrawerParamList>();
|
||||
const status = useSelector((state: RootState) => state.status);
|
||||
const dispatch = useDispatch();
|
||||
const queryClient = useQueryClient();
|
||||
const toast = useToast();
|
||||
const debug_disable_clear_on_logout = true;
|
||||
const stop_studying_logout = useMutation({
|
||||
mutationFn: async (info: StudentStatusPatchType) => {
|
||||
const data = await PatchStudentStatus(info);
|
||||
if (data[0] != true) {
|
||||
return Promise.reject(new Error());
|
||||
}
|
||||
console.log("DEBUG", data);
|
||||
return data;
|
||||
},
|
||||
onSuccess: async () => {
|
||||
toast.show("Logged out. Stopped studying", {
|
||||
type: "warning",
|
||||
placement: "top",
|
||||
duration: 2000,
|
||||
animationType: "slide-in",
|
||||
});
|
||||
queryClient.clear();
|
||||
dispatch(logout());
|
||||
await AsyncStorage.clear();
|
||||
navigation.navigate("Login");
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.show(String(error), {
|
||||
type: "warning",
|
||||
placement: "top",
|
||||
duration: 2000,
|
||||
animationType: "slide-in",
|
||||
});
|
||||
},
|
||||
});
|
||||
if (status.logged_in && status.onboarding) {
|
||||
return (
|
||||
<DrawerContentScrollView {...props}>
|
||||
|
@ -38,9 +78,16 @@ export default function CustomDrawerContent(props: {}) {
|
|||
|
||||
<DrawerButton
|
||||
onPress={async () => {
|
||||
dispatch(logout());
|
||||
await AsyncStorage.clear();
|
||||
navigation.navigate("Login");
|
||||
if (debug_disable_clear_on_logout) {
|
||||
queryClient.clear();
|
||||
dispatch(logout());
|
||||
await AsyncStorage.clear();
|
||||
navigation.navigate("Login");
|
||||
} else {
|
||||
stop_studying_logout.mutate({
|
||||
active: false,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<LogoutIcon size={32} />
|
||||
|
@ -86,9 +133,16 @@ export default function CustomDrawerContent(props: {}) {
|
|||
</DrawerButton>
|
||||
<DrawerButton
|
||||
onPress={async () => {
|
||||
dispatch(logout());
|
||||
await AsyncStorage.clear();
|
||||
navigation.navigate("Login");
|
||||
if (debug_disable_clear_on_logout) {
|
||||
queryClient.clear();
|
||||
dispatch(logout());
|
||||
await AsyncStorage.clear();
|
||||
navigation.navigate("Login");
|
||||
} else {
|
||||
stop_studying_logout.mutate({
|
||||
active: false,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<LogoutIcon size={32} />
|
||||
|
|
20
src/components/GetDistance/GetDistanceFromUSTP.tsx
Normal file
20
src/components/GetDistance/GetDistanceFromUSTP.tsx
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { LocationType } from "../../interfaces/Interfaces";
|
||||
import GetDistance from "./GetDistance";
|
||||
|
||||
export default function GetDistanceFromUSTP(location: LocationType) {
|
||||
const ustpCoords = {
|
||||
latitude: 8.4857,
|
||||
longitude: 124.6565,
|
||||
latitudeDelta: 0.000235,
|
||||
longitudeDelta: 0.000067,
|
||||
};
|
||||
|
||||
let dist = GetDistance(
|
||||
location.latitude,
|
||||
location.longitude,
|
||||
ustpCoords.latitude,
|
||||
ustpCoords.longitude
|
||||
);
|
||||
dist = Math.round(dist);
|
||||
return dist;
|
||||
}
|
79
src/components/MapRenderer/MapRendererFar.tsx
Normal file
79
src/components/MapRenderer/MapRendererFar.tsx
Normal file
|
@ -0,0 +1,79 @@
|
|||
import * as React from "react";
|
||||
import { View, Text } from "react-native";
|
||||
import MapView, { UrlTile, Callout, Marker } from "react-native-maps";
|
||||
import styles, { Viewport, colors } from "../../styles";
|
||||
import { urlProvider } from "../Api/Api";
|
||||
import { LocationType, RawLocationType } from "../../interfaces/Interfaces";
|
||||
import GetDistance from "../../components/GetDistance/GetDistance";
|
||||
|
||||
type props = {
|
||||
location: LocationType;
|
||||
dist: any;
|
||||
};
|
||||
|
||||
export default function MapRendererFar(props: props) {
|
||||
return (
|
||||
<>
|
||||
<Text style={styles.text_white_medium}>
|
||||
You are too far from USTP {"\n"}
|
||||
Get closer to use Stud-E
|
||||
</Text>
|
||||
<MapView
|
||||
style={{
|
||||
height: Viewport.height * 0.5,
|
||||
width: Viewport.width * 0.8,
|
||||
alignSelf: "center",
|
||||
}}
|
||||
customMapStyle={[
|
||||
{
|
||||
featureType: "poi",
|
||||
stylers: [
|
||||
{
|
||||
visibility: "off",
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
mapType="none"
|
||||
scrollEnabled={false}
|
||||
zoomEnabled={false}
|
||||
toolbarEnabled={false}
|
||||
rotateEnabled={false}
|
||||
minZoomLevel={18}
|
||||
initialRegion={{
|
||||
latitude: props.location.latitude,
|
||||
longitude: props.location.longitude,
|
||||
latitudeDelta: 0.0922,
|
||||
longitudeDelta: 0.0421,
|
||||
}}
|
||||
loadingBackgroundColor={colors.secondary_2}
|
||||
>
|
||||
<UrlTile
|
||||
urlTemplate={urlProvider}
|
||||
shouldReplaceMapContent={true}
|
||||
maximumZ={19}
|
||||
flipY={false}
|
||||
zIndex={1}
|
||||
/>
|
||||
<Marker
|
||||
coordinate={{
|
||||
latitude: props.location.latitude,
|
||||
longitude: props.location.longitude,
|
||||
}}
|
||||
pinColor={colors.primary_1}
|
||||
>
|
||||
<Callout>
|
||||
<Text style={styles.text_black_tiny}>
|
||||
You are here {"\n"}
|
||||
X: {Math.round(props.location.longitude) + "\n"}
|
||||
Z: {Math.round(props.location.latitude)}
|
||||
</Text>
|
||||
</Callout>
|
||||
</Marker>
|
||||
</MapView>
|
||||
<Text style={styles.text_white_small}>
|
||||
{props.dist}km away from USTP {"\n"}
|
||||
</Text>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
export default function ParseError(text: string) {
|
||||
if (text) {
|
||||
return text
|
||||
.replaceAll(/[{}()"]/g, " ")
|
||||
.replaceAll(/,/g, "\n")
|
||||
.replaceAll("[", "")
|
||||
.replaceAll("]", "")
|
||||
.replaceAll(".", "");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
export function ParseLoginError(text: string) {
|
||||
if (text) {
|
||||
return text
|
||||
.replaceAll(/[{}()"]/g, " ")
|
||||
.replaceAll(/,/g, "\n")
|
||||
.replaceAll("[", "")
|
||||
.replaceAll("]", "")
|
||||
.replaceAll(".", "")
|
||||
.replaceAll("non_field_errors", "");
|
||||
}
|
||||
return "";
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue