From bbffea76a355275227e587e2afa3731e445d00de Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Sat, 30 Sep 2023 17:48:48 +0800 Subject: [PATCH 01/10] Fix messages page button visibility --- .../DrawerSettings/CustomDrawerContent.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/DrawerSettings/CustomDrawerContent.tsx b/src/components/DrawerSettings/CustomDrawerContent.tsx index cfd622a..0d91bd4 100644 --- a/src/components/DrawerSettings/CustomDrawerContent.tsx +++ b/src/components/DrawerSettings/CustomDrawerContent.tsx @@ -131,6 +131,14 @@ export default function CustomDrawerContent(props: {}) { Subjects + { + navigation.navigate("Conversation"); + }} + > + + Conversation + { if (debug_disable_clear_on_logout) { @@ -178,14 +186,6 @@ export default function CustomDrawerContent(props: {}) { Register - { - navigation.navigate("Conversation"); - }} - > - - Conversation - {/* Debug buttons for accessing revalidation and activation page Date: Sat, 30 Sep 2023 18:31:26 +0800 Subject: [PATCH 02/10] Fixed homepage sometimes not rendering when not studying --- src/routes/Home/Home.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/routes/Home/Home.tsx b/src/routes/Home/Home.tsx index c1b7ff0..3f0e256 100644 --- a/src/routes/Home/Home.tsx +++ b/src/routes/Home/Home.tsx @@ -264,7 +264,6 @@ export default function Home() { useState([]); // Student Status List Global const StudentStatusListGlobalQuery = useQuery({ - enabled: !studying, queryKey: ["user_status_list_global"], queryFn: async () => { const data = await GetStudentStatusList(); @@ -323,7 +322,6 @@ export default function Home() { >([]); // Study Group Global List const StudyGroupGlobalQuery = useQuery({ - enabled: !studying, queryKey: ["study_group_list_global"], queryFn: async () => { const data = await GetStudyGroupList(); From 63f863fa1ef6add3ebc2ba90fafb882a7ed74ec5 Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Sat, 30 Sep 2023 18:40:46 +0800 Subject: [PATCH 03/10] Revert fix to homepage and clean up code for messages page --- .../ConversationPage/ConversationPage.tsx | 226 ++++++------------ src/routes/Home/Home.tsx | 10 +- src/styles.tsx | 20 +- 3 files changed, 99 insertions(+), 157 deletions(-) diff --git a/src/routes/ConversationPage/ConversationPage.tsx b/src/routes/ConversationPage/ConversationPage.tsx index c8340da..6c00b9b 100644 --- a/src/routes/ConversationPage/ConversationPage.tsx +++ b/src/routes/ConversationPage/ConversationPage.tsx @@ -1,179 +1,105 @@ import * as React from "react"; import styles from "../../styles"; -import { - View, - Text, - TextInput, - ScrollView, - StyleSheet -} from "react-native"; +import { View, Text, TextInput, ScrollView, StyleSheet } from "react-native"; import { colors } from "../../styles"; import { useState } from "react"; -const convStyles = StyleSheet.create({ - scrollViewContainer: { - backgroundColor: colors.secondary_1, - padding: 15, - }, - messageContainer: { - backgroundColor: '#00000038', - margin: 5, - padding: 10, - borderRadius: 20, - }, - badge: { - height: 10, - width: 10, - borderRadius: 10, - } -}) +const convStyles = StyleSheet.create({}); type ConversationType = { - username: string; - message: string; - userId: number; - type: string; + id: number; + user: string; + message_content: string; + study_group: string; color: string; -} - +}; export default function ConversationPage() { - const [conversation, setConversation] = useState([{ - username: "You", - message: "Hello World naa ko diri canteen gutom sh*t.", - userId: Math.floor(Math.random() * 1000), - type: "o", - color: Math.floor(Math.random() * 16777215).toString(16) - }, { - username: "User 2", - message: "Hahahah shor oy.", - userId: Math.floor(Math.random() * 1000), - type: "i", - color: Math.floor(Math.random() * 16777215).toString(16) - }, { - username: "User 3", - message: "AHAHAHHA BOBO!", - userId: Math.floor(Math.random() * 1000), - type: "i", - color: Math.floor(Math.random() * 16777215).toString(16) - }, - { - username: "Vale", - message: "tanga valir! bobo!", - userId: Math.floor(Math.random() * 1000), - type: "i", - color: Math.floor(Math.random() * 16777215).toString(16) - }, - { - username: "You", - message: "Hoyy bobo!!!", - userId: Math.floor(Math.random() * 1000), - type: "o", - color: Math.floor(Math.random() * 16777215).toString(16) - }, { - username: "Valir", - message: "Gago! 1v1 nalng?", - userId: Math.floor(Math.random() * 1000), - type: "o", - color: Math.floor(Math.random() * 16777215).toString(16) - }, { - username: "User 2", - message: "Hello World", - userId: Math.floor(Math.random() * 1000), - type: "i", - color: Math.floor(Math.random() * 16777215).toString(16) - }, { - username: "User 3", - message: "Hello World", - userId: Math.floor(Math.random() * 1000), - type: "i", - color: Math.floor(Math.random() * 16777215).toString(16) - }, - { - username: "User 1", - message: "Hello World", - userId: Math.floor(Math.random() * 1000), - type: "o", - color: Math.floor(Math.random() * 16777215).toString(16) - }, - { - username: "User 1", - message: "Hello World", - userId: Math.floor(Math.random() * 1000), - type: "o", - color: Math.floor(Math.random() * 16777215).toString(16) - }, - { - username: "User 1", - message: "Hello World", - userId: Math.floor(Math.random() * 1000), - type: "o", - color: Math.floor(Math.random() * 16777215).toString(16) - }, - { - username: "User 1", - message: "Hello World", - userId: Math.floor(Math.random() * 1000), - type: "o", - color: Math.floor(Math.random() * 16777215).toString(16) - } + const [conversation, setConversation] = useState([ + { + user: "You", + message_content: "Hello World naa ko diri canteen gutom sh*t.", + id: Math.floor(Math.random() * 1000), + color: Math.floor(Math.random() * 16777215).toString(16), + study_group: "Heh group", + }, + { + user: "User 2", + message_content: "Hahahah shor oy.", + id: Math.floor(Math.random() * 1000), + color: Math.floor(Math.random() * 16777215).toString(16), + study_group: "Heh group", + }, ]); return ( - - + + - + Group#57605 - 3 students - - - + + 3 students + + + - { - conversation.map((item: ConversationType) => ( - + {conversation.map((item: ConversationType, index: number) => { + const color = `rgba(${Math.floor( + Math.random() * 256 + )}, ${Math.floor(Math.random() * 256)}, ${Math.floor( + Math.random() * 256 + )}, 0.7)`; + return ( + + + {index % 2 == 0 ? ( + + ) : ( + + )} + {item.user} + - { - item.type == 'i' ? : null - } - {item.username} - { - item.type == 'o' ? : null - } - - - {item.message} + {item.message_content} - )) - } + ); + })} + style={styles.chatbox} + placeholder="type here...." + placeholderTextColor="white" + autoCapitalize="none" + /> ); } diff --git a/src/routes/Home/Home.tsx b/src/routes/Home/Home.tsx index 3f0e256..3f72156 100644 --- a/src/routes/Home/Home.tsx +++ b/src/routes/Home/Home.tsx @@ -121,17 +121,13 @@ export default function Home() { return data; }, onSuccess: (data: StudentStatusReturnType) => { - 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"); } + setSubject(data[1].subject); + setStudying(data[1].active); setStudentStatus(data[1]); }, onError: (error: Error) => { @@ -264,6 +260,7 @@ export default function Home() { useState([]); // Student Status List Global const StudentStatusListGlobalQuery = useQuery({ + enabled: !studying, queryKey: ["user_status_list_global"], queryFn: async () => { const data = await GetStudentStatusList(); @@ -322,6 +319,7 @@ export default function Home() { >([]); // Study Group Global List const StudyGroupGlobalQuery = useQuery({ + enabled: !studying, queryKey: ["study_group_list_global"], queryFn: async () => { const data = await GetStudyGroupList(); diff --git a/src/styles.tsx b/src/styles.tsx index 0f9ab15..8e8f1cf 100644 --- a/src/styles.tsx +++ b/src/styles.tsx @@ -212,6 +212,24 @@ const styles = StyleSheet.create({ backgroundColor: colors.primary_2, borderRadius: 20, borderColor: colors.primary_3, - } + }, + messageScrollViewContainer: { + backgroundColor: colors.secondary_1, + padding: 15, + }, + message_contentContainer: { + backgroundColor: "#00000038", + margin: 5, + padding: 10, + borderRadius: 20, + }, + badge: { + height: 16, + width: 16, + justifyContent: "center", + borderRadius: 10, + marginLeft: 4, + marginRight: 4, + }, }); export default styles; From 2cd770e5e10e162504b7cf1dd6fb7fed02cded27 Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Sun, 1 Oct 2023 00:54:31 +0800 Subject: [PATCH 04/10] Improved functionlity of messages page --- package-lock.json | 9 + package.json | 1 + src/components/Api/Api.tsx | 55 ++- src/interfaces/Interfaces.tsx | 24 +- .../ConversationPage/ConversationPage.tsx | 394 ++++++++++++++---- src/routes/Home/Home.tsx | 16 +- src/styles.tsx | 11 + 7 files changed, 414 insertions(+), 96 deletions(-) diff --git a/package-lock.json b/package-lock.json index e918be3..c490eda 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "expo-linking": "~4.0.1", "expo-location": "~15.1.1", "expo-status-bar": "~1.4.4", + "moment": "^2.29.4", "moti": "^0.25.3", "react": "18.2.0", "react-native": "0.71.8", @@ -11123,6 +11124,14 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, "node_modules/moti": { "version": "0.25.3", "resolved": "https://registry.npmjs.org/moti/-/moti-0.25.3.tgz", diff --git a/package.json b/package.json index df97e93..5b3b404 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "expo-linking": "~4.0.1", "expo-location": "~15.1.1", "expo-status-bar": "~1.4.4", + "moment": "^2.29.4", "moti": "^0.25.3", "react": "18.2.0", "react-native": "0.71.8", diff --git a/src/components/Api/Api.tsx b/src/components/Api/Api.tsx index 0eaad7d..e6111a9 100644 --- a/src/components/Api/Api.tsx +++ b/src/components/Api/Api.tsx @@ -4,6 +4,7 @@ import { ActivationType, LocationType, LoginType, + MessagePostType, OnboardingType, PatchUserInfoType, RegistrationType, @@ -311,7 +312,6 @@ export async function GetStudyGroupListFiltered() { return instance .get("/api/v1/study_groups/near/", config) .then((response) => { - console.log("DEBUGGG", response.data); return [true, response.data]; }) .catch((error) => { @@ -346,3 +346,56 @@ export async function CreateStudyGroup(info: StudyGroupCreateType) { return [false, error_message]; }); } + +export async function GetStudyGroup(name: string) { + const config = await GetConfig(); + return instance + .get(`/api/v1/study_groups/${name}`, config) + .then((response) => { + return [true, response.data]; + }) + .catch((error) => { + let error_message = ParseError(error); + return [false, error_message]; + }); +} + +export async function GetStudyGroupMessages() { + const config = await GetConfig(); + return instance + .get(`/api/v1/messages/`, config) + .then((response) => { + return [true, response.data]; + }) + .catch((error) => { + let error_message = ParseError(error); + return [false, error_message]; + }); +} + +export async function GetStudyGroupMemberAvatars() { + const config = await GetConfig(); + return instance + .get(`/api/v1/study_groups/member_avatars`, config) + .then((response) => { + return [true, response.data]; + }) + .catch((error) => { + let error_message = ParseError(error); + return [false, error_message]; + }); +} + +export async function PostMessage(info: MessagePostType) { + const config = await GetConfig(); + return instance + .post(`/api/v1/messages/`, info, config) + .then((response) => { + return [true, response.data]; + }) + .catch((error) => { + console.log("Error:", error.response.data); + let error_message = ParseError(error); + return [false, error_message]; + }); +} diff --git a/src/interfaces/Interfaces.tsx b/src/interfaces/Interfaces.tsx index ae61d63..717dc4d 100644 --- a/src/interfaces/Interfaces.tsx +++ b/src/interfaces/Interfaces.tsx @@ -185,13 +185,31 @@ export interface StudyGroupCreateType { subject: string; } +export interface MessageType { + id: number; + user: string; + study_group: string; + message_content: string; + timestamp: string; +} + +export interface MessagePostType { + message_content: string; +} + +export interface GroupMessageAvatarType { + username: string; + avatar: string; +} + +export type GroupMessageAvatarListType = GroupMessageAvatarType[]; +export type GroupMessageAvatarReturnType = [boolean, GroupMessageAvatarType[]]; +export type MessageReturnType = [boolean, MessageType[]]; +export type StudyGroupDetailReturnType = [boolean, StudyGroupType]; export type StudyGroupReturnType = [boolean, StudyGroupType[]]; - export type StudentStatusReturnType = [boolean, StudentStatusType]; - export type StudentStatusListType = Array; export type StudentStatusListReturnType = [boolean, StudentStatusListType]; - export type RawLocationType = Location.LocationObject; export interface UserInfoType { diff --git a/src/routes/ConversationPage/ConversationPage.tsx b/src/routes/ConversationPage/ConversationPage.tsx index 6c00b9b..e7ce17c 100644 --- a/src/routes/ConversationPage/ConversationPage.tsx +++ b/src/routes/ConversationPage/ConversationPage.tsx @@ -1,10 +1,38 @@ import * as React from "react"; +import { ActivityIndicator, Image } from "react-native"; import styles from "../../styles"; -import { View, Text, TextInput, ScrollView, StyleSheet } from "react-native"; +import { + View, + Text, + TextInput, + ScrollView, + NativeSyntheticEvent, + TextInputChangeEventData, +} from "react-native"; import { colors } from "../../styles"; -import { useState } from "react"; - -const convStyles = StyleSheet.create({}); +import { useRef, useState } from "react"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { + GetStudentStatus, + GetStudyGroup, + GetStudyGroupMemberAvatars, + GetStudyGroupMessages, + PostMessage, +} from "../../components/Api/Api"; +import { + StudentStatusType, + StudentStatusReturnType, + StudyGroupType, + StudyGroupDetailReturnType, + MessageType, + MessageReturnType, + MessagePostType, + GroupMessageAvatarType, + GroupMessageAvatarReturnType, +} from "../../interfaces/Interfaces"; +import { useToast } from "react-native-toast-notifications"; +import { useQueryClient } from "@tanstack/react-query"; +import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer"; type ConversationType = { id: number; @@ -15,91 +43,293 @@ type ConversationType = { }; export default function ConversationPage() { - const [conversation, setConversation] = useState([ - { - user: "You", - message_content: "Hello World naa ko diri canteen gutom sh*t.", - id: Math.floor(Math.random() * 1000), - color: Math.floor(Math.random() * 16777215).toString(16), - study_group: "Heh group", + const toast = useToast(); + // Student Status + const [student_status, setStudentStatus] = useState(); + const StudentStatusQuery = useQuery({ + queryKey: ["user_status"], + queryFn: async () => { + const data = await GetStudentStatus(); + if (data[0] == false) { + return Promise.reject(new Error(JSON.stringify(data[1]))); + } + return data; }, - { - user: "User 2", - message_content: "Hahahah shor oy.", - id: Math.floor(Math.random() * 1000), - color: Math.floor(Math.random() * 16777215).toString(16), - study_group: "Heh group", + onSuccess: (data: StudentStatusReturnType) => { + setStudentStatus(data[1]); }, - ]); + onError: (error: Error) => { + toast.show(String(error), { + type: "warning", + placement: "top", + duration: 2000, + animationType: "slide-in", + }); + }, + }); - return ( - - - - - Group#57605 - - - 3 students - - - - - - - {conversation.map((item: ConversationType, index: number) => { - const color = `rgba(${Math.floor( - Math.random() * 256 - )}, ${Math.floor(Math.random() * 256)}, ${Math.floor( - Math.random() * 256 - )}, 0.7)`; - return ( - (); + const StudyGroupQuery = useQuery({ + enabled: + student_status?.study_group != "" && student_status?.study_group != null, + queryKey: ["study_group"], + queryFn: async () => { + const data = await GetStudyGroup(student_status?.study_group || ""); + if (data[0] == false) { + return Promise.reject(new Error(JSON.stringify(data[1]))); + } + return data; + }, + onSuccess: (data: StudyGroupDetailReturnType) => { + if (data[1]) { + setStudyGroup(data[1]); + } + }, + onError: (error: Error) => { + toast.show(String(error), { + type: "warning", + placement: "top", + duration: 2000, + animationType: "slide-in", + }); + }, + }); + + // Study Group Messages + const [messages, setMessages] = useState([]); + const MessageQuery = useQuery({ + refetchInterval: 3000, + enabled: + !StudentStatusQuery.isLoading && + (student_status?.study_group != null || + student_status?.study_group != ""), + queryKey: ["study_group_messages"], + queryFn: async () => { + const data = await GetStudyGroupMessages(); + if (data[0] == false) { + return Promise.reject(new Error(JSON.stringify(data[1]))); + } + return data; + }, + onSuccess: (data: MessageReturnType) => { + if (data[1]) { + setMessages(data[1]); + } + }, + onError: (error: Error) => { + toast.show(String(error), { + type: "warning", + placement: "top", + duration: 2000, + animationType: "slide-in", + }); + }, + }); + + // Avatar List + const [users, setUsers] = useState([]); + const AvatarsQuery = useQuery({ + refetchInterval: 3000, + enabled: + student_status?.study_group != null || + (student_status?.study_group != "" && + studygroup != null && + studygroup.students != null), + queryKey: ["study_group_avatars"], + queryFn: async () => { + const data = await GetStudyGroupMemberAvatars(); + if (data[0] == false) { + return Promise.reject(new Error(JSON.stringify(data[1]))); + } + return data; + }, + onSuccess: (data: GroupMessageAvatarReturnType) => { + if (data[1]) { + setUsers(data[1]); + } + }, + onError: (error: Error) => { + toast.show(String(error), { + type: "warning", + placement: "top", + duration: 2000, + animationType: "slide-in", + }); + }, + }); + const scrollViewRef = useRef(null); + const queryClient = useQueryClient(); + const [message, setMessage] = useState(""); + const send_message = useMutation({ + mutationFn: async (info: MessagePostType) => { + const data = await PostMessage(info); + if (data[0] != true) { + return Promise.reject(new Error()); + } + return data; + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["study_group_messages"] }); + }, + onError: (error: Error) => { + toast.show(String(error), { + type: "warning", + placement: "top", + duration: 2000, + animationType: "slide-in", + }); + }, + }); + + if ( + !StudyGroupQuery.isLoading && + !AvatarsQuery.isLoading && + !MessageQuery.isLoading && + student_status && + studygroup && + studygroup.students + ) { + return ( + + + + + + {`Group: ${studygroup?.name ? studygroup.name : ""}`} + + + + - - {index % 2 == 0 ? ( - - ) : ( - - )} - {item.user} - + {studygroup.students.length} studying + + {users.map((user: GroupMessageAvatarType, index: number) => { + if (index > 6) { + return ; + } + return ( + + {user.avatar != null && user.avatar != "" ? ( + + ) : ( + + )} + + ); + })} + + + scrollViewRef.current?.scrollToEnd()} + ref={scrollViewRef} + > + {messages.length > 0 ? ( + messages.map((message: MessageType, index: number) => { + let avatar = ""; + users.filter((user: GroupMessageAvatarType) => { + if (user.username == message.user) { + avatar = user.avatar; + } + }); + return ( + + + {avatar != null && avatar != "" ? ( + + ) : ( + + )} - - {item.message_content} - - - ); - })} - + + {message.user} + + + {message.timestamp} + + + + + {message.message_content} + + + ); + }) + ) : ( + There are no messages + )} + + + ): void => { + setMessage(e.nativeEvent.text); + }} + onSubmitEditing={() => { + send_message.mutate({ + message_content: message, + }); + setMessage(""); + }} + /> + - - + ); + } else if (!student_status?.study_group) { + return ( + + + + You are not in a study group. Join one to start a conversation! + + + + ); + } + return ( + + + + Loading... + + ); } diff --git a/src/routes/Home/Home.tsx b/src/routes/Home/Home.tsx index 3f72156..c562fa5 100644 --- a/src/routes/Home/Home.tsx +++ b/src/routes/Home/Home.tsx @@ -82,18 +82,14 @@ export default function Home() { } } - // Refresh every 15 seconds - useEffect(() => { - const interval = setInterval(() => { - requestLocation(); - }, 15000); - - return () => clearInterval(interval); - }); - // Refresh when screen loads useEffect(() => { + // Refresh every 15 seconds + const interval = setInterval(async () => { + await requestLocation(); + }, 15000); requestLocation(); + return () => clearInterval(interval); }, []); async function DistanceHandler(location: RawLocationType) { @@ -829,7 +825,7 @@ export default function Home() { return ( <> {feedback} - diff --git a/src/styles.tsx b/src/styles.tsx index 8e8f1cf..af0d3fa 100644 --- a/src/styles.tsx +++ b/src/styles.tsx @@ -186,6 +186,16 @@ const styles = StyleSheet.create({ borderColor: colors.primary_2, borderWidth: 3, }, + profile_mini: { + height: 32, + width: 32, + alignSelf: "center", + borderRadius: 150 / 2, + overflow: "hidden", + padding: 0, + borderColor: colors.primary_2, + borderWidth: 3, + }, input: { paddingHorizontal: 8, marginVertical: 2, @@ -212,6 +222,7 @@ const styles = StyleSheet.create({ backgroundColor: colors.primary_2, borderRadius: 20, borderColor: colors.primary_3, + width: 256, }, messageScrollViewContainer: { backgroundColor: colors.secondary_1, From d072ae456dcdf0cdf3e1cc70249059f258c8f6ad Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Sun, 1 Oct 2023 14:28:03 +0800 Subject: [PATCH 05/10] Added background notifications --- App.tsx | 3 +- package-lock.json | 327 ++++++++++++++++++ package.json | 5 +- .../BackgroundTask/BackgroundTask.tsx | 84 +++++ 4 files changed, 417 insertions(+), 2 deletions(-) create mode 100644 src/components/BackgroundTask/BackgroundTask.tsx diff --git a/App.tsx b/App.tsx index 25bc1c6..650e714 100644 --- a/App.tsx +++ b/App.tsx @@ -28,6 +28,7 @@ import StartStudying from "./src/routes/StartStudying/StartStudying"; import { ToastProvider } from "react-native-toast-notifications"; import InfoIcon from "./src/icons/InfoIcon/InfoIcon"; import CreateGroup from "./src/routes/CreateGroup/CreateGroup"; +import BackgroundComponent from "./src/components/BackgroundTask/BackgroundTask"; const Drawer = createDrawerNavigator(); @@ -66,11 +67,11 @@ export default function App() { icon={} textStyle={{ ...styles.text_white_tiny_bold }} > + - }> = 4.5.0" } }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/axios": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", @@ -5775,6 +5806,11 @@ "@babel/core": "^7.0.0" } }, + "node_modules/badgin": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/badgin/-/badgin-1.2.3.tgz", + "integrity": "sha512-NQGA7LcfCpSzIbGRbkgjgdWkjy7HI+Th5VLxTJfW5EeaAf3fnS+xWQaQOCYiny+q6QSvxqoSO04vCx+4u++EJw==" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -6921,6 +6957,19 @@ "node": ">=0.8" } }, + "node_modules/define-data-property": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz", + "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==", + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -6929,6 +6978,22 @@ "node": ">=8" } }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/define-property": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", @@ -7435,6 +7500,17 @@ "url-parse": "^1.5.9" } }, + "node_modules/expo-background-fetch": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/expo-background-fetch/-/expo-background-fetch-11.1.1.tgz", + "integrity": "sha512-5X63ogpCqEqdqXYk4Sl4J8Lt/ZWcQCboS1pq3uprqD1KUi4ICl1P3gwPo+il3rYPV6xYb74Wv1KyyEEYj2vklg==", + "dependencies": { + "expo-task-manager": "~11.1.0" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-constants": { "version": "14.2.1", "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-14.2.1.tgz", @@ -7645,11 +7721,74 @@ "invariant": "^2.2.4" } }, + "node_modules/expo-notifications": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/expo-notifications/-/expo-notifications-0.18.1.tgz", + "integrity": "sha512-lOEiuPE6ubkS5u7Nj/57gkmUGD/MxsRTC6bg9SGJqXIitBQZk3Tmv9y8bjTrn71n7DsrH8K7xCZTbVwr+kLQGg==", + "dependencies": { + "@expo/image-utils": "^0.3.18", + "@ide/backoff": "^1.0.0", + "abort-controller": "^3.0.0", + "assert": "^2.0.0", + "badgin": "^1.1.5", + "expo-application": "~5.1.0", + "expo-constants": "~14.2.0", + "fs-extra": "^9.1.0", + "uuid": "^3.4.0" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-notifications/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/expo-notifications/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/expo-notifications/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/expo-status-bar": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-1.4.4.tgz", "integrity": "sha512-5DV0hIEWgatSC3UgQuAZBoQeaS9CqeWRZ3vzBR9R/+IUD87Adbi4FGhU10nymRqFXOizGsureButGZIXPs7zEA==" }, + "node_modules/expo-task-manager": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/expo-task-manager/-/expo-task-manager-11.1.1.tgz", + "integrity": "sha512-Ot4wq0fVd8+I1W7MsJz0rNdX0ma/zdnBvAppxDX1Oo0o0exo4qs1FmgrTnh3OBnn18aB4cX3wBJoXIatIgNMZQ==", + "dependencies": { + "unimodules-app-loader": "~4.1.0" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", @@ -7955,6 +8094,14 @@ "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz", "integrity": "sha512-6FPvD/IVyT4ZlNe7Wcn5Fb/4ChigpucKYSvD6a+0iMoLn2inpo711eyIcKjmDtE5XNcgAkSH9uN/nfAeZzHEfg==" }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, "node_modules/for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -8187,6 +8334,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -8233,6 +8391,17 @@ "node": ">=4" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", @@ -8255,6 +8424,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -8587,6 +8770,21 @@ "node": ">=0.10.0" } }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -8597,6 +8795,17 @@ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-core-module": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", @@ -8681,6 +8890,20 @@ "node": ">=4" } }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -8730,6 +8953,21 @@ "node": ">=0.10.0" } }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -8789,6 +9027,20 @@ "node": ">=0.10.0" } }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -11499,6 +11751,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/object-visit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", @@ -11510,6 +11785,23 @@ "node": ">=0.10.0" } }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -14237,6 +14529,11 @@ "node": ">=4" } }, + "node_modules/unimodules-app-loader": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/unimodules-app-loader/-/unimodules-app-loader-4.1.2.tgz", + "integrity": "sha512-DLYUCjNpguFhNpxNsBf47NWZi2OjIfWCVQY4f+r4/bwIqFfR7qQd6lXwCFA8EhHS1ti87CBzjMSbIC5bB0J/0Q==" + }, "node_modules/union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -14428,6 +14725,18 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -14532,6 +14841,24 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" }, + "node_modules/which-typed-array": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/wonka": { "version": "4.0.15", "resolved": "https://registry.npmjs.org/wonka/-/wonka-4.0.15.tgz", diff --git a/package.json b/package.json index 5b3b404..d742989 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,10 @@ "react-native-toast-notifications": "^3.3.1", "react-query": "^3.39.3", "react-redux": "^8.1.1", - "redux": "^4.2.1" + "redux": "^4.2.1", + "expo-task-manager": "~11.1.1", + "expo-background-fetch": "~11.1.1", + "expo-notifications": "~0.18.1" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/src/components/BackgroundTask/BackgroundTask.tsx b/src/components/BackgroundTask/BackgroundTask.tsx new file mode 100644 index 0000000..21f3ebd --- /dev/null +++ b/src/components/BackgroundTask/BackgroundTask.tsx @@ -0,0 +1,84 @@ +import React, { useEffect } from "react"; +import { View } from "react-native"; +import * as BackgroundFetch from "expo-background-fetch"; +import * as TaskManager from "expo-task-manager"; +import * as Notifications from "expo-notifications"; +import { + GetStudentStatus, + GetStudyGroupListFiltered, + GetUserInfo, +} from "../Api/Api"; +import { StudyGroupType } from "../../interfaces/Interfaces"; + +const FETCH_STUDENT_STATUS = "STUDENT_STATUS_TASK"; + +TaskManager.defineTask(FETCH_STUDENT_STATUS, async () => { + const data = await GetStudyGroupListFiltered(); + const student_status_data = await GetStudentStatus(); + if (data[0] && data[1].length > -1) { + console.log("Background Fetch", data[1]); + const entryWithLeastDistance = data[1].reduce( + (prev: StudyGroupType, curr: StudyGroupType) => { + return prev.distance < curr.distance ? prev : curr; + } + ); + // Only display a notification if a student isn't in a study group yet + if ( + student_status_data[1].study_group == null || + student_status_data[1].study_group == "" + ) { + Notifications.scheduleNotificationAsync({ + content: { + title: "Students are studying nearby", + body: `Nearest study group is ${Math.round( + entryWithLeastDistance.distance * 1000 + )}m away`, + }, + trigger: { + seconds: 1, + }, + }); + } + } else { + console.log(data[1].response.data); + } + + return BackgroundFetch.BackgroundFetchResult.NewData; +}); + +const BackgroundComponent = () => { + const [isRegistered, setIsRegistered] = React.useState(false); + const [status, setStatus] = React.useState(); + const checkStatusAsync = async () => { + const status = await BackgroundFetch.getStatusAsync(); + const isRegistered = await TaskManager.isTaskRegisteredAsync( + FETCH_STUDENT_STATUS + ); + setStatus(status); + setIsRegistered(isRegistered); + }; + + useEffect(() => { + const registerTask = async () => { + try { + await checkStatusAsync(); + if (!isRegistered) { + await BackgroundFetch.registerTaskAsync(FETCH_STUDENT_STATUS, { + minimumInterval: 5, // seconds, + }); + console.log("Task registered"); + } else { + console.log("Task already registered"); + } + } catch (err) { + console.log("Task Register failed:", err); + } + }; + + registerTask(); + }, []); + + return ; +}; + +export default BackgroundComponent; From a9d7188c670cdabdca0a9ffcc0e2ee10c27b92d4 Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Sun, 1 Oct 2023 14:32:25 +0800 Subject: [PATCH 06/10] Re-separate use effect for periodic checking and on load checking in homepage --- src/routes/Home/Home.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/routes/Home/Home.tsx b/src/routes/Home/Home.tsx index c562fa5..331b6ef 100644 --- a/src/routes/Home/Home.tsx +++ b/src/routes/Home/Home.tsx @@ -82,14 +82,18 @@ export default function Home() { } } + // Refresh every 15 seconds + useEffect(() => { + const interval = setInterval(() => { + requestLocation(); + }, 15000); + + return () => clearInterval(interval); + }); + // Refresh when screen loads useEffect(() => { - // Refresh every 15 seconds - const interval = setInterval(async () => { - await requestLocation(); - }, 15000); requestLocation(); - return () => clearInterval(interval); }, []); async function DistanceHandler(location: RawLocationType) { From b0345dc2b730114277243508c4041af4c6b43910 Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Sun, 1 Oct 2023 15:56:01 +0800 Subject: [PATCH 07/10] Code cleanup --- src/components/BackgroundTask/BackgroundTask.tsx | 9 +++------ src/components/DrawerSettings/CustomDrawerContent.tsx | 8 +++++--- src/routes/ConversationPage/ConversationPage.tsx | 8 -------- src/routes/Home/Home.tsx | 6 +++--- 4 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/components/BackgroundTask/BackgroundTask.tsx b/src/components/BackgroundTask/BackgroundTask.tsx index 21f3ebd..840319c 100644 --- a/src/components/BackgroundTask/BackgroundTask.tsx +++ b/src/components/BackgroundTask/BackgroundTask.tsx @@ -3,11 +3,7 @@ import { View } from "react-native"; import * as BackgroundFetch from "expo-background-fetch"; import * as TaskManager from "expo-task-manager"; import * as Notifications from "expo-notifications"; -import { - GetStudentStatus, - GetStudyGroupListFiltered, - GetUserInfo, -} from "../Api/Api"; +import { GetStudentStatus, GetStudyGroupListFiltered } from "../Api/Api"; import { StudyGroupType } from "../../interfaces/Interfaces"; const FETCH_STUDENT_STATUS = "STUDENT_STATUS_TASK"; @@ -47,6 +43,7 @@ TaskManager.defineTask(FETCH_STUDENT_STATUS, async () => { }); const BackgroundComponent = () => { + const notification_debug = true; const [isRegistered, setIsRegistered] = React.useState(false); const [status, setStatus] = React.useState(); const checkStatusAsync = async () => { @@ -64,7 +61,7 @@ const BackgroundComponent = () => { await checkStatusAsync(); if (!isRegistered) { await BackgroundFetch.registerTaskAsync(FETCH_STUDENT_STATUS, { - minimumInterval: 5, // seconds, + minimumInterval: notification_debug ? 5 : 60 * 3, // Check every 5 seconds in dev & every 3 minutes in production builds }); console.log("Task registered"); } else { diff --git a/src/components/DrawerSettings/CustomDrawerContent.tsx b/src/components/DrawerSettings/CustomDrawerContent.tsx index 0d91bd4..1b31ad8 100644 --- a/src/components/DrawerSettings/CustomDrawerContent.tsx +++ b/src/components/DrawerSettings/CustomDrawerContent.tsx @@ -27,12 +27,12 @@ import { PatchStudentStatus } from "../Api/Api"; import { useToast } from "react-native-toast-notifications"; export default function CustomDrawerContent(props: {}) { + const debug = true; const navigation = useNavigation(); 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); @@ -78,7 +78,8 @@ export default function CustomDrawerContent(props: {}) { { - if (debug_disable_clear_on_logout) { + // We don't clear student statuses when logging out on debug + if (!debug) { queryClient.clear(); dispatch(logout()); await AsyncStorage.clear(); @@ -141,7 +142,8 @@ export default function CustomDrawerContent(props: {}) { { - if (debug_disable_clear_on_logout) { + // We don't clear student statuses when logging out on debug + if (!debug) { queryClient.clear(); dispatch(logout()); await AsyncStorage.clear(); diff --git a/src/routes/ConversationPage/ConversationPage.tsx b/src/routes/ConversationPage/ConversationPage.tsx index e7ce17c..edc8982 100644 --- a/src/routes/ConversationPage/ConversationPage.tsx +++ b/src/routes/ConversationPage/ConversationPage.tsx @@ -34,14 +34,6 @@ import { useToast } from "react-native-toast-notifications"; import { useQueryClient } from "@tanstack/react-query"; import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer"; -type ConversationType = { - id: number; - user: string; - message_content: string; - study_group: string; - color: string; -}; - export default function ConversationPage() { const toast = useToast(); // Student Status diff --git a/src/routes/Home/Home.tsx b/src/routes/Home/Home.tsx index 331b6ef..8e3cbd7 100644 --- a/src/routes/Home/Home.tsx +++ b/src/routes/Home/Home.tsx @@ -38,7 +38,7 @@ import CaretUpIcon from "../../icons/CaretUpIcon/CaretUpIcon"; export default function Home() { // Switch this condition to see the main map when debugging - const map_debug = true; + const map_distance_override = true; const navigation = useNavigation(); const [location, setLocation] = useState(null); const [dist, setDist] = useState(null); @@ -100,7 +100,7 @@ export default function Home() { let dist = GetDistanceFromUSTP(location.coords); setDist(dist); // Deactivate student status if too far away - if (dist >= 2 && !map_debug) + if (dist >= 2 && !map_distance_override) stop_studying.mutate({ active: false, }); @@ -345,7 +345,7 @@ export default function Home() { function CustomMap() { if (dist && location) { - if (dist <= 2 || map_debug) { + if (dist <= 2 || map_distance_override) { return ( <> Date: Sun, 1 Oct 2023 15:57:45 +0800 Subject: [PATCH 08/10] Added potential fix to homepage map elements sometimes not rendering on startup --- src/routes/Home/Home.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/routes/Home/Home.tsx b/src/routes/Home/Home.tsx index 8e3cbd7..ecdbfab 100644 --- a/src/routes/Home/Home.tsx +++ b/src/routes/Home/Home.tsx @@ -228,7 +228,7 @@ export default function Home() { useState([]); // Student Status List const StudentStatusListQuery = useQuery({ - enabled: studying, + enabled: studying && !StudentStatusQuery.isLoading, queryKey: ["user_status_list"], queryFn: async () => { const data = await GetStudentStatusListNear(); @@ -260,7 +260,7 @@ export default function Home() { useState([]); // Student Status List Global const StudentStatusListGlobalQuery = useQuery({ - enabled: !studying, + enabled: !studying && !StudentStatusQuery.isLoading, queryKey: ["user_status_list_global"], queryFn: async () => { const data = await GetStudentStatusList(); @@ -291,7 +291,7 @@ export default function Home() { const [study_groups, setStudyGroups] = useState([]); // Study Group List const StudyGroupQuery = useQuery({ - enabled: studying, + enabled: studying && !StudentStatusQuery.isLoading, queryKey: ["study_group_list"], queryFn: async () => { const data = await GetStudyGroupListFiltered(); @@ -319,7 +319,7 @@ export default function Home() { >([]); // Study Group Global List const StudyGroupGlobalQuery = useQuery({ - enabled: !studying, + enabled: !studying && !StudentStatusQuery.isLoading, queryKey: ["study_group_list_global"], queryFn: async () => { const data = await GetStudyGroupList(); From 2461f2c40465580dae2cb038558d8de9736de4b0 Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Sun, 1 Oct 2023 16:29:36 +0800 Subject: [PATCH 09/10] Fixed pullup menu wording on homepage and added message notifications --- .../BackgroundTask/BackgroundTask.tsx | 79 ++++++++++++++++--- .../ConversationPage/ConversationPage.tsx | 4 +- src/routes/Home/Home.tsx | 17 ++-- 3 files changed, 80 insertions(+), 20 deletions(-) diff --git a/src/components/BackgroundTask/BackgroundTask.tsx b/src/components/BackgroundTask/BackgroundTask.tsx index 840319c..70f4515 100644 --- a/src/components/BackgroundTask/BackgroundTask.tsx +++ b/src/components/BackgroundTask/BackgroundTask.tsx @@ -3,15 +3,55 @@ import { View } from "react-native"; import * as BackgroundFetch from "expo-background-fetch"; import * as TaskManager from "expo-task-manager"; import * as Notifications from "expo-notifications"; -import { GetStudentStatus, GetStudyGroupListFiltered } from "../Api/Api"; +import { + GetStudentStatus, + GetStudyGroupListFiltered, + GetStudyGroupMessages, +} from "../Api/Api"; import { StudyGroupType } from "../../interfaces/Interfaces"; +import AsyncStorage from "@react-native-async-storage/async-storage"; const FETCH_STUDENT_STATUS = "STUDENT_STATUS_TASK"; +const FETCH_GROUP_MESSAGES = "GROUP_MESSAGES_TASK"; + +TaskManager.defineTask(FETCH_GROUP_MESSAGES, async () => { + const data = await GetStudyGroupMessages(); + if (data[0] && data[1]) { + let messages_prev = JSON.parse( + (await AsyncStorage.getItem("messages")) || "{}" + ); + if (!messages_prev) { + await AsyncStorage.setItem("messages", JSON.stringify(data[1])); + } else { + let message_curr = data[1]; + let difference: Array = messages_prev + .filter((x: any) => !message_curr.includes(x)) + .concat(message_curr.filter((x: any) => !messages_prev.includes(x))); + + if (difference.length > 0) { + console.log(`${difference.length} unread messages`); + Notifications.scheduleNotificationAsync({ + content: { + title: `${difference.length} unread messages`, + body: `${difference[0].user}: ${difference[0].message_content}`, + }, + trigger: { + seconds: 1, + }, + }); + } + } + } else { + console.log(data[1].response.data); + } + + return BackgroundFetch.BackgroundFetchResult.NewData; +}); TaskManager.defineTask(FETCH_STUDENT_STATUS, async () => { const data = await GetStudyGroupListFiltered(); const student_status_data = await GetStudentStatus(); - if (data[0] && data[1].length > -1) { + if (data[0] && data[1]) { console.log("Background Fetch", data[1]); const entryWithLeastDistance = data[1].reduce( (prev: StudyGroupType, curr: StudyGroupType) => { @@ -44,35 +84,50 @@ TaskManager.defineTask(FETCH_STUDENT_STATUS, async () => { const BackgroundComponent = () => { const notification_debug = true; - const [isRegistered, setIsRegistered] = React.useState(false); + const [Task1_isRegistered, Task1_setIsRegistered] = React.useState(false); + const [Task2_isRegistered, Task2_setIsRegistered] = React.useState(false); const [status, setStatus] = React.useState(); const checkStatusAsync = async () => { - const status = await BackgroundFetch.getStatusAsync(); - const isRegistered = await TaskManager.isTaskRegisteredAsync( + let status = await BackgroundFetch.getStatusAsync(); + setStatus(status); + let Task1_isRegistered = await TaskManager.isTaskRegisteredAsync( FETCH_STUDENT_STATUS ); - setStatus(status); - setIsRegistered(isRegistered); + let Task2_isRegistered = await TaskManager.isTaskRegisteredAsync( + FETCH_GROUP_MESSAGES + ); + Task1_setIsRegistered(Task1_isRegistered); + Task2_setIsRegistered(Task2_isRegistered); }; useEffect(() => { - const registerTask = async () => { + const registerTasks = async () => { try { await checkStatusAsync(); - if (!isRegistered) { + // Nearby students task + if (!Task1_isRegistered) { await BackgroundFetch.registerTaskAsync(FETCH_STUDENT_STATUS, { minimumInterval: notification_debug ? 5 : 60 * 3, // Check every 5 seconds in dev & every 3 minutes in production builds }); - console.log("Task registered"); + console.log("Task for nearby students check registered"); } else { - console.log("Task already registered"); + console.log("Task for nearby students check already registered"); + } + // Message Checking Task + if (!Task2_isRegistered) { + await BackgroundFetch.registerTaskAsync(FETCH_GROUP_MESSAGES, { + minimumInterval: notification_debug ? 5 : 30, // Check every 5 seconds in dev & every 30 seconds in production builds + }); + console.log("Task for group messages check registered"); + } else { + console.log("Task for group messages check already registered"); } } catch (err) { console.log("Task Register failed:", err); } }; - registerTask(); + registerTasks(); }, []); return ; diff --git a/src/routes/ConversationPage/ConversationPage.tsx b/src/routes/ConversationPage/ConversationPage.tsx index edc8982..fdba4cf 100644 --- a/src/routes/ConversationPage/ConversationPage.tsx +++ b/src/routes/ConversationPage/ConversationPage.tsx @@ -33,6 +33,7 @@ import { import { useToast } from "react-native-toast-notifications"; import { useQueryClient } from "@tanstack/react-query"; import AnimatedContainer from "../../components/AnimatedContainer/AnimatedContainer"; +import AsyncStorage from "@react-native-async-storage/async-storage"; export default function ConversationPage() { const toast = useToast(); @@ -104,8 +105,9 @@ export default function ConversationPage() { } return data; }, - onSuccess: (data: MessageReturnType) => { + onSuccess: async (data: MessageReturnType) => { if (data[1]) { + await AsyncStorage.setItem("messages", JSON.stringify(data[1])); setMessages(data[1]); } }, diff --git a/src/routes/Home/Home.tsx b/src/routes/Home/Home.tsx index ecdbfab..bf002f8 100644 --- a/src/routes/Home/Home.tsx +++ b/src/routes/Home/Home.tsx @@ -845,7 +845,6 @@ export default function Home() { hasBackdrop={false} > - Groups List - { - setModalByGroup(!modalByGroup); - }} - /> + + { + setModalByGroup(!modalByGroup); + }} + /> + List View + + {!modalByGroup ? ( student_statuses.map( From fa07743a90be24cb36713ddd20e417e003966b2f Mon Sep 17 00:00:00 2001 From: Keannu Bernasol Date: Sun, 1 Oct 2023 16:39:43 +0800 Subject: [PATCH 10/10] Fixed group message notifications always triggering --- .../BackgroundTask/BackgroundTask.tsx | 56 +++++++++++-------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/src/components/BackgroundTask/BackgroundTask.tsx b/src/components/BackgroundTask/BackgroundTask.tsx index 70f4515..57cb25b 100644 --- a/src/components/BackgroundTask/BackgroundTask.tsx +++ b/src/components/BackgroundTask/BackgroundTask.tsx @@ -17,29 +17,38 @@ const FETCH_GROUP_MESSAGES = "GROUP_MESSAGES_TASK"; TaskManager.defineTask(FETCH_GROUP_MESSAGES, async () => { const data = await GetStudyGroupMessages(); if (data[0] && data[1]) { - let messages_prev = JSON.parse( - (await AsyncStorage.getItem("messages")) || "{}" + let messages_prev = await JSON.parse( + (await AsyncStorage.getItem("messages")) || "[]" ); - if (!messages_prev) { - await AsyncStorage.setItem("messages", JSON.stringify(data[1])); - } else { - let message_curr = data[1]; - let difference: Array = messages_prev - .filter((x: any) => !message_curr.includes(x)) - .concat(message_curr.filter((x: any) => !messages_prev.includes(x))); + await AsyncStorage.setItem("messages", JSON.stringify(data[1])); + let message_curr = data[1]; + let difference: Array = messages_prev + .filter( + (x: any) => + !message_curr.some( + (y: any) => JSON.stringify(y) === JSON.stringify(x) + ) + ) + .concat( + message_curr.filter( + (x: any) => + !messages_prev.some( + (y: any) => JSON.stringify(y) === JSON.stringify(x) + ) + ) + ); - if (difference.length > 0) { - console.log(`${difference.length} unread messages`); - Notifications.scheduleNotificationAsync({ - content: { - title: `${difference.length} unread messages`, - body: `${difference[0].user}: ${difference[0].message_content}`, - }, - trigger: { - seconds: 1, - }, - }); - } + if (difference.length > 0) { + console.log(`${difference.length} unread messages`); + Notifications.scheduleNotificationAsync({ + content: { + title: `${difference.length} unread messages`, + body: `${difference[0].user}: ${difference[0].message_content}`, + }, + trigger: { + seconds: 1, + }, + }); } } else { console.log(data[1].response.data); @@ -52,7 +61,7 @@ TaskManager.defineTask(FETCH_STUDENT_STATUS, async () => { const data = await GetStudyGroupListFiltered(); const student_status_data = await GetStudentStatus(); if (data[0] && data[1]) { - console.log("Background Fetch", data[1]); + console.log("Fetching nearby study groups..."); const entryWithLeastDistance = data[1].reduce( (prev: StudyGroupType, curr: StudyGroupType) => { return prev.distance < curr.distance ? prev : curr; @@ -63,6 +72,9 @@ TaskManager.defineTask(FETCH_STUDENT_STATUS, async () => { student_status_data[1].study_group == null || student_status_data[1].study_group == "" ) { + console.log( + "User has no study group yet. Found nearby groups, pushing notification" + ); Notifications.scheduleNotificationAsync({ content: { title: "Students are studying nearby",