Skip to content

Commit

Permalink
Merge pull request #237 from COS301-SE-2024/feat/integration/notifica…
Browse files Browse the repository at this point in the history
…tions

Feat/integration/notifications
  • Loading branch information
KamogeloMoeketse authored Jul 22, 2024
2 parents 819cf4c + aa2939a commit 538a77d
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 28 deletions.
2 changes: 1 addition & 1 deletion frontend/occupi-mobile4/screens/Login/SplashScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default function SplashScreen() {
useEffect(() => {
const timer = setTimeout(() => {
setSelectedIndex(1); // Assuming Onboarding1 is at index 1
router.replace('/login'); // Navigate to Onboarding1 screen
router.replace('/notifications'); // Navigate to Onboarding1 screen
}, 5000); // 8 seconds

return () => clearTimeout(timer); // Clean up timer on component unmount
Expand Down
157 changes: 147 additions & 10 deletions frontend/occupi-mobile4/screens/Notifications/Notifications.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,166 @@
import { useState, useEffect } from 'react';
import Navbar from '../../components/NavBar';
import {
Text,
View,
Image,
Card,
Toast,
useToast,
ToastTitle,
Button,
ButtonText,
Divider,
ScrollView
} from '@gluestack-ui/themed';
import * as SecureStore from 'expo-secure-store';
import { StatusBar, useColorScheme, Dimensions } from 'react-native';
import { Entypo } from '@expo/vector-icons';
import { AntDesign, Entypo, FontAwesome6 } from '@expo/vector-icons';
import { Skeleton } from 'moti/skeleton';
import axios from 'axios';

const Notifications = () => {
const colorScheme = useColorScheme();
const toast = useToast();
const [notifications, setNotifications] = useState();
const [loading, setLoading] = useState(true);
const todayNotifications = [];
const yesterdayNotifications = [];
const olderNotifications = [];

const apiUrl = process.env.EXPO_PUBLIC_DEVELOP_API_URL;

const formatNotificationDate = (sendTime) => {
const now = new Date();
// console.log(now);
const notificationDate = new Date(sendTime);
console.log(notificationDate);

const differenceInHours = Math.floor((now - notificationDate) / (1000 * 60 * 60));
const differenceInDays = Math.floor(differenceInHours / 24);

if (differenceInDays === 0) {
console.log(differenceInDays);
return differenceInHours < 1 ? 'less than an hour ago' : `${differenceInHours} hours ago`;
} else if (differenceInDays === 1) {
return 'yesterday';
} else {
return notificationDate.toLocaleDateString();
}
};

if (notifications) {
notifications.forEach(notification => {
const formattedDate = formatNotificationDate(notification.send_time);

if (formattedDate.includes('hours ago') || formattedDate.includes('hour ago')) {
todayNotifications.push(notification);
} else if (formattedDate === 'yesterday') {
yesterdayNotifications.push(notification);
} else {
olderNotifications.push(notification);
}
});
}


useEffect(() => {
const getNotifications = async () => {
let userEmail = await SecureStore.getItemAsync('Email');
let authToken = await SecureStore.getItemAsync('Token');

try {
const response = await axios.get('https://dev.occupi.tech/api/get-notifications', {
params: {
filter: {
emails: [{ userEmail }]
},
order_desc: "send_time"
},
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': `${authToken}`
},
withCredentials: true
});
const data = response.data;
// console.log(`Response Data: ${JSON.stringify(data.data)}`);
console.log(data);
if (response.status === 200) {
setNotifications(data.data || []); // Ensure data is an array
setLoading(false);
} else {
console.log(data);
setLoading(false);
toast.show({
placement: 'top',
render: ({ id }) => {
return (
<Toast nativeID={id} variant="accent" action="error">
<ToastTitle>{data.error.message}</ToastTitle>
</Toast>
);
},
});
}
} catch (error) {
console.error('Error:', error);
toast.show({
placement: 'top',
render: ({ id }) => {
return (
<Toast nativeID={id} variant="accent" action="error">
<ToastTitle>Network Error: {error.message}</ToastTitle>
</Toast>
);
},
});
}
};
getNotifications();
}, [apiUrl, toast])

const renderNotifications = (notificationList) => (
notificationList.map((notification, idx) => (
<View key={idx}>
<View pr="$2" flexDirection='row' alignItems='center'>
<AntDesign name={notification.title === "Booking Invitation" ? "addusergroup" : "clockcircleo"} size={40} color={colorScheme === 'dark' ? '#FFFFFF' : '#000000'} />
<Text pl={16} pr="$4" py={4} style={{ color: colorScheme === 'dark' ? '#FFFFFF' : '#000000' }}>
{notification.message} · <Text style={{ color: 'grey' }}>{formatNotificationDate(notification.send_time)}</Text>
</Text>
</View>
</View>
))
);

return (

<View pt="$20" px="$4" flex={1} flexDirection="column" backgroundColor={colorScheme === 'dark' ? '$black' : '$white'}>
<View flexDirection='row' justifyContent='space-between'>
<View flexDirection='row' justifyContent='space-between' mb="$2">
<Text fontWeight="$bold" fontSize={28} color={colorScheme === 'dark' ? '$white' : '$black'}>Notifications</Text>
<View style={{ backgroundColor: '#ADFF2F', alignItems: 'center', padding: 8, borderRadius: 12 }}>
<Entypo name="sound-mix" size={26} color="black"style={{ transform: [{ rotate: '90deg' }] }}/>
</View>
<View style={{ backgroundColor: '#ADFF2F', alignItems: 'center', padding: 8, borderRadius: 12 }}>
<Entypo name="sound-mix" size={26} color="black" style={{ transform: [{ rotate: '90deg' }] }} />
</View>
</View>

{loading === true ? (
<>
{Array.from({ length: 8 }, (_, index) => (
<View mt={index === 0 ? '$4' : '$2'}>
<Skeleton colorMode={colorScheme === 'dark' ? 'dark' : 'light'} height={80} width={"100%"} />
</View>
))}
</>
) : (
<ScrollView>
<View>
<Text mb="$2" style={{ fontWeight: 'bold', fontSize: 16 }} color={colorScheme === 'dark' ? '$white' : '$black'}>Recent</Text>
{renderNotifications(todayNotifications)}
<Divider my="$2" bgColor='grey' />
<Text my="$2" style={{ fontWeight: 'bold', fontSize: 16 }} color={colorScheme === 'dark' ? '$white' : '$black'}>Yesterday</Text>
{renderNotifications(yesterdayNotifications)}
<Divider my="$2" bgColor='grey' />
<Text my="$2" style={{ fontWeight: 'bold', fontSize: 16 }} color={colorScheme === 'dark' ? '$white' : '$black'}>Older</Text>
{renderNotifications(olderNotifications)}
</View>
</ScrollView>
)}
<Navbar />
</View>
)
Expand Down
60 changes: 44 additions & 16 deletions frontend/occupi-mobile4/screens/Office/BookingDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const BookingDetails = () => {
const [startTime, setStartTime] = useState('');
const [endTime, setEndTime] = useState('');
const isDark = colorScheme === "dark";
const [pushTokens, setPushTokens] = useState([]);
// console.log(creatorEmail + roomId + floorNo);
// console.log(bookingInfo?);
// console.log(startTime);
Expand All @@ -57,7 +58,7 @@ const BookingDetails = () => {
let userinfo = await SecureStore.getItemAsync('UserData');
// if (result !== undefined) {
let jsoninfo = JSON.parse(userinfo);
console.log("data",jsoninfo?.data.details.name);
console.log("data", jsoninfo?.data.details.name);
setCreatorEmail(jsoninfo?.data?.email);
let result: string = await SecureStore.getItemAsync('BookingInfo');
console.log("CurrentRoom:", jsoninfo?.data?.email);
Expand Down Expand Up @@ -106,27 +107,51 @@ const BookingDetails = () => {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'Authorization': `${authToken}`
'Authorization': `${authToken}`,
'X-Timezone': 'Africa/Johannesburg'
},
body: JSON.stringify(body),
credentials: "include"
});
const data = await response.json();
console.log(data);
console.log(attendees);
if (response.ok) {
sendPushNotification(['ExponentPushToken[5cpRYINQu42bhcKM5b7Vsb]','ExponentPushToken[ARLofOIiMGuJjE2EQTWQWq]'], "New Booking", `${jsoninfo?.data.details.name} has invited you to a booking.`);
setCurrentStep(2);
setLoading(false);
toast.show({
placement: 'top',
render: ({ id }) => {
return (
<Toast nativeID={String(id)} variant="accent" action="success">
<ToastTitle>{data.message}</ToastTitle>
</Toast>
);
},
});
try {
const response = await fetch(`${apiUrl}/api/get-push-tokens?emails=${attendees.slice(1)}`, {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'Authorization': `${authToken}`,
'X-Timezone': 'Africa/Johannesburg'
},
credentials: "include"
});
const data = await response.json();
console.log("PUSHH TOKENSS",data);
if (data.data) {
let tokens = data.data.map((item) => item.expoPushToken);
setPushTokens(tokens);
console.log(tokens);
sendPushNotification(tokens, "New Booking", `${jsoninfo?.data.details.name} has invited you to a booking.`);
}
setCurrentStep(2);
setLoading(false);
toast.show({
placement: 'top',
render: ({ id }) => {
return (
<Toast nativeID={String(id)} variant="accent" action="success">
<ToastTitle>{data.message}</ToastTitle>
</Toast>
);
},
});
} catch (error) {
setLoading(false);
console.error('Error:', error);
}
} else {
console.log(data);
setLoading(false);
Expand All @@ -142,6 +167,7 @@ const BookingDetails = () => {
});
}
} catch (error) {
setLoading(false);
console.error('Error:', error);
// setResponse('An error occurred');
}
Expand Down Expand Up @@ -237,6 +263,8 @@ const BookingDetails = () => {
const handleBiometricAuth = async () => {
const hasHardware = await LocalAuthentication.hasHardwareAsync();
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
const biometricType = await LocalAuthentication.supportedAuthenticationTypesAsync();
console.log('Supported biometric types:', biometricType);

if (!hasHardware || !isEnrolled) {
Alert.alert(
Expand Down Expand Up @@ -508,7 +536,7 @@ const BookingDetails = () => {
</View>
<ScrollView style={{ height: 70 }}>
{attendees.map((email, idx) => (
<Text color={isDark ? '#fff' : '#000'}>{idx + 1}. {email}</Text>
<Text key={idx} color={isDark ? '#fff' : '#000'}>{idx + 1}. {email}</Text>
))}
</ScrollView>
</View>
Expand Down
2 changes: 1 addition & 1 deletion frontend/occupi-mobile4/screens/Settings/NotifTester.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ async function sendPushNotification(expoPushToken: string) {
trigger: {
seconds: 10
},
to: "ExponentPushToken[ARLofOIiMGuJjE2EQTWQWq]",
to: expoPushToken,
sound: 'default',
title: 'Original Title',
body: 'And here is the body!',
Expand Down

0 comments on commit 538a77d

Please sign in to comment.