pogdark-app/app/StatusPage.tsx
2025-02-19 13:33:09 -05:00

195 lines
9.5 KiB
TypeScript

import React, { useEffect, useState } from "react";
import useWebSocket from "react-use-websocket";
import axios from "axios";
import { Image, StyleSheet, View } from "react-native";
import { Appbar, Avatar, Button, List, useTheme, Dialog, Portal, Text, Menu } from "react-native-paper";
interface Message {
Id: string;
Name: string;
Image: string;
Status: string;
Timestamp: string;
}
const styles = StyleSheet.create({
container: { flex: 1, alignItems: "stretch" },
logoContainer: { flex: 1, alignItems: "center" },
logo: { width: 150, height: 75, resizeMode: "contain" },
listContainer: { flex: 1, width: "100%" },
listSubheader: {
fontSize: 18, // Larger text
textAlign: "center", // Center the text
fontWeight: "bold", // Make it more distinct
marginBottom: 10, // Add spacing below
},
listWrapper: { flex: 1, padding: 5 },
listRow: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "flex-start", // Aligns subheaders properly
paddingHorizontal: 10, // Adds some spacing
},
listColumn: { flex: 1, paddingHorizontal: 5 },
buttonContainer: {
flexDirection: "row",
justifyContent: "space-between",
alignSelf: "stretch",
paddingHorizontal: 10,
paddingBottom: 20,
},
actionButton: {
flex: 1,
marginHorizontal: 5,
},
card: {
marginVertical: 5,
elevation: 4, // Android shadow
shadowColor: "#000", // iOS shadow
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 4,
borderRadius: 10
},
});
const StatusPage = ({ toggleProfile, id, name, image, isProfileActive }: { toggleProfile: () => void; id: string; name: string; image: string; isProfileActive: boolean }) => {
const theme = useTheme();
const [messages, setMessages] = useState<Message[]>([]);
const { lastMessage } = useWebSocket("ws://localhost:8080/ws", {
shouldReconnect: () => true,
});
const [aboutVisible, setAboutVisible] = useState(false);
const [menuVisible, setMenuVisible] = useState(false);
const [menuAnchor, setMenuAnchor] = useState<{ x: number; y: number } | null>(null);
useEffect(() => {
if (lastMessage?.data) {
try {
const newMessage: Message = JSON.parse(lastMessage.data);
setMessages((prev) => {
if (newMessage.Status === "removed") {
return prev.filter((msg) => msg.Id !== newMessage.Id);
}
return prev.filter((msg) => msg.Id !== newMessage.Id).concat(newMessage);
});
} catch (error) {
console.error("Error parsing WebSocket message:", error);
}
}
}, [lastMessage]);
const sendStatus = async (status: string) => {
try {
const message: Message = { Id: id, Name: name, Image: image, Status: status, Timestamp: new Date().toISOString() };
await axios.post("http://localhost:8080/set", message);
} catch (error) {
console.error("Error sending status:", error);
}
};
return (
<View style={[styles.container, { backgroundColor: theme.colors.background }, isProfileActive && { display: 'none' }]}>
<Appbar.Header style={{ backgroundColor: theme.colors.primary }}>
<View>
<Menu
visible={menuVisible}
onDismiss={() => setMenuVisible(false)}
anchor={menuAnchor}
>
<Menu.Item
onPress={() => {
setMenuVisible(false);
setAboutVisible(true);
}}
title="About Us"
/>
</Menu>
<Appbar.Action
icon="menu"
onPressIn={(event) => {
setMenuAnchor({ x: event.nativeEvent.pageX, y: event.nativeEvent.pageY + 40 });
setMenuVisible(true);
}}
iconColor={theme.colors.inversePrimary}
/>
</View>
<View style={styles.logoContainer}>
<Image source={require("../assets/images/pogdark_logo.png")} style={styles.logo} />
</View>
<Appbar.Action icon="pencil" onPress={toggleProfile} iconColor={ theme.colors.inversePrimary } />
</Appbar.Header>
<View style={[styles.listContainer, { backgroundColor: theme.colors.surface }]}>
<View style={styles.listRow}>
<View style={styles.listColumn}>
<List.Section>
<List.Subheader style={[styles.listSubheader, { color: theme.colors.onSurface }]}>On the Way</List.Subheader>
{messages.filter(msg => msg.Status === "On the Way")
.sort((a, b) => new Date(a.Timestamp).getTime() - new Date(b.Timestamp).getTime())
.map(item => (
<View key={item.Id} style={[styles.card, { backgroundColor: theme.colors.primaryContainer }]}>
<List.Item
key={item.Id}
title={item.Name}
titleStyle={{ color: theme.colors.onSurface }}
description={new Date(item.Timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: true })}
descriptionStyle={{ color: theme.colors.onSurface }}
left={(props) => <Avatar.Image {...props} size={40} source={{ uri: `data:image/png;base64,${item.Image}` }} />}
/>
</View>
))}
</List.Section>
</View>
<View style={styles.listColumn}>
<List.Section>
<List.Subheader style={[styles.listSubheader, { color: theme.colors.onSurface }]}>Arrived</List.Subheader>
{messages.filter(msg => msg.Status === "Arrived")
.sort((a, b) => new Date(a.Timestamp).getTime() - new Date(b.Timestamp).getTime())
.map(item => (
<View key={item.Id} style={[styles.card, { backgroundColor: theme.colors.primaryContainer }]}>
<List.Item
key={item.Id}
title={item.Name}
titleStyle={{ color: theme.colors.onSurface }}
description={new Date(item.Timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: true })}
descriptionStyle={{ color: theme.colors.onSurface }}
left={(props) => <Avatar.Image {...props} size={40} source={{ uri: `data:image/png;base64,${item.Image}` }} />}
/>
</View>
))}
</List.Section>
</View>
</View>
</View>
<View style={styles.buttonContainer}>
<Button mode="contained" onPress={() => sendStatus("On the Way")}
style={[styles.actionButton, { backgroundColor: theme.colors.primary }]}
labelStyle={{ color: theme.colors.onPrimary }}>On the Way</Button>
<Button mode="contained" onPress={() => sendStatus("Arrived")}
style={[styles.actionButton, { backgroundColor: theme.colors.primary }]}
labelStyle={{ color: theme.colors.onPrimary }} >Arrived</Button>
</View>
<Portal>
<Dialog visible={aboutVisible} onDismiss={() => setAboutVisible(false)} style={{ backgroundColor: theme.colors.background }}>
<Dialog.Title style={{ color: theme.colors.onBackground, textAlign: 'center' }}>About Us</Dialog.Title>
<Dialog.Content>
<Text style={{ color: theme.colors.onSurface, textAlign: 'justify' }}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam auctor, nisl nec laoreet luctus, felis sapien facilisis augue, at tristique enim turpis nec turpis. Donec convallis justo vel mi consectetur, at pulvinar justo dictum. Mauris vulputate dapibus neque, sed sagittis libero dapibus eu. Integer nec mi at quam cursus suscipit sed et tortor.
</Text>
</Dialog.Content>
<Dialog.Actions style={{ justifyContent: "center" }}>
<Button onPress={() => setAboutVisible(false)} mode="contained" style={{ backgroundColor: theme.colors.secondary }} labelStyle={{ color: theme.colors.onPrimary }}>
Close
</Button>
</Dialog.Actions>
</Dialog>
</Portal>
</View>
);
};
export default StatusPage;