Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/integration/notifications #237

Merged
merged 9 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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';

Check warning on line 13 in frontend/occupi-mobile4/screens/Notifications/Notifications.tsx

View workflow job for this annotation

GitHub Actions / lint

'StatusBar' is defined but never used

Check warning on line 13 in frontend/occupi-mobile4/screens/Notifications/Notifications.tsx

View workflow job for this annotation

GitHub Actions / lint

'Dimensions' is defined but never used
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
Loading