diff --git a/app.config.ts b/app.config.ts new file mode 100644 index 0000000..174e238 --- /dev/null +++ b/app.config.ts @@ -0,0 +1,12 @@ +import 'dotenv/config'; + +let config = { + API_URL: process.env.EXPO_PUBLIC_API_URL, + WS_URL: process.env.EXPO_PUBLIC_WS_URL, +} + +export default { + extra: { + ...config, + }, +}; diff --git a/app.json b/app.json index e775390..e9d0c7a 100644 --- a/app.json +++ b/app.json @@ -1,7 +1,7 @@ { "expo": { - "name": "pogdark_app_rn", - "slug": "pogdark_app_rn", + "name": "Pogdark", + "slug": "Pogdark", "version": "1.0.0", "orientation": "portrait", "icon": "./assets/images/icon.png", diff --git a/app/StatusPage.tsx b/app/StatusPage.tsx index d8fd215..f816239 100644 --- a/app/StatusPage.tsx +++ b/app/StatusPage.tsx @@ -1,9 +1,12 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useState, useRef } from "react"; import useWebSocket from "react-use-websocket"; import axios from "axios"; -import { StyleSheet, View } from "react-native"; +import { Animated, Easing, ImageBackground, StyleSheet, View } from "react-native"; import { Avatar, List, Button, useTheme, } from "react-native-paper"; +export const API_URL = process.env.EXPO_PUBLIC_API_URL; +export const WS_URL = process.env.EXPO_PUBLIC_WS_URL; + interface Message { Id: string; Name: string; @@ -14,12 +17,13 @@ interface Message { const styles = StyleSheet.create({ container: { flex: 1, alignItems: "stretch" }, - listContainer: { flex: 1, width: "100%" }, + listContainer: { flex: 1, width: "100%", backgroundColor: 'transparent' }, listSubheader: { fontSize: 18, // Larger text textAlign: "center", // Center the text fontWeight: "bold", // Make it more distinct marginBottom: 10, // Add spacing below + zIndex: 0, }, listWrapper: { flex: 1, padding: 5 }, listRow: { @@ -28,7 +32,7 @@ const styles = StyleSheet.create({ alignItems: "flex-start", // Aligns subheaders properly paddingHorizontal: 10, // Adds some spacing }, - listColumn: { flex: 1, paddingHorizontal: 5 }, + listColumn: { flex: 1, paddingHorizontal: 5, zIndex: 1}, buttonContainer: { flexDirection: "row", justifyContent: "space-between", @@ -47,24 +51,97 @@ const styles = StyleSheet.create({ shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, shadowRadius: 4, - borderRadius: 10 + borderRadius: 10, + }, + imageBackground: { + position: "absolute", // Allows child elements to layer on top + width: "100%", // Ensure full coverage of the column + height: "100%", // Fully stretches to column height + resizeMode: "cover", // Ensures it fits well + opacity: 0.2, // Fades the image + zIndex: -1, }, }); -const StatusPage = ({ id, name, image, isProfileActive }: { id: string; name: string; image: string; isProfileActive: boolean }) => { - const theme = useTheme(); +interface StatusProps { + id: string; + name: string; + image: string; + currentStatus: string; + setStatus: (currentStatus: string) => void; + isProfileActive: boolean; +} +const StatusPage: React.FC = ({ id, name, image, currentStatus, setStatus, isProfileActive }) => { + const theme = useTheme(); const [messages, setMessages] = useState([]); - const { lastMessage } = useWebSocket("ws://localhost:8080/ws", { + const { lastMessage } = useWebSocket(WS_URL + "/ws", { shouldReconnect: () => true, }); + // Animated values for background color pulsing + const pulseAnimOnTheWay = useRef(new Animated.Value(0)).current; + const pulseAnimArrived = useRef(new Animated.Value(0)).current; + + // Function to trigger the color pulsing animation + const startPulsing = (animationRef: Animated.Value) => { + Animated.loop( + Animated.sequence([ + Animated.timing(animationRef, { + toValue: 1, + duration: 800, + easing: Easing.inOut(Easing.ease), + useNativeDriver: false, + }), + Animated.timing(animationRef, { + toValue: 0, + duration: 800, + easing: Easing.inOut(Easing.ease), + useNativeDriver: false, + }), + ]) + ).start(); + }; + + useEffect(() => { + console.log("Updated status: ", currentStatus); + + if (currentStatus === "On the Way") { + startPulsing(pulseAnimOnTheWay); + } else { + pulseAnimOnTheWay.setValue(0); // Reset animation + } + + if (currentStatus === "Arrived") { + startPulsing(pulseAnimArrived); + } else { + pulseAnimArrived.setValue(0); // Reset animation + } + }, [currentStatus]); + + // Interpolated colors for pulsing effect + const pulseColorOnTheWay = pulseAnimOnTheWay.interpolate({ + inputRange: [0, 1], + outputRange: [theme.colors.secondary, theme.colors.primary], // From active color to normal color + }); + + const pulseColorArrived = pulseAnimArrived.interpolate({ + inputRange: [0, 1], + outputRange: [theme.colors.secondary, theme.colors.primary], + }); + + useEffect(() => { if (lastMessage?.data) { try { const newMessage: Message = JSON.parse(lastMessage.data); setMessages((prev) => { if (newMessage.Status === "removed") { + if (newMessage.Id === id) { + setTimeout(() => { + setStatus(""); // Correctly clears the status + }, 0); + } return prev.filter((msg) => msg.Id !== newMessage.Id); } return prev.filter((msg) => msg.Id !== newMessage.Id).concat(newMessage); @@ -73,12 +150,13 @@ const StatusPage = ({ id, name, image, isProfileActive }: { id: string; name: st console.error("Error parsing WebSocket message:", error); } } - }, [lastMessage]); + }, [lastMessage, setStatus, id]); 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); + await axios.post(API_URL + "/set", message); + setStatus(status) } catch (error) { console.error("Error sending status:", error); } @@ -86,7 +164,7 @@ const StatusPage = ({ id, name, image, isProfileActive }: { id: string; name: st return ( - + @@ -128,17 +206,34 @@ const StatusPage = ({ id, name, image, isProfileActive }: { id: string; name: st + - - + + + + + + - - ); }; diff --git a/app/index.tsx b/app/index.tsx index 27bbed9..020762c 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { View, StyleSheet } from "react-native"; +import {View, StyleSheet } from "react-native"; import { useTheme } from "react-native-paper"; import { v4 as uuidv4 } from "uuid"; import AsyncStorage from "@react-native-async-storage/async-storage"; @@ -13,6 +13,7 @@ const Index = () => { const [userId, setUserId] = useState(uuidv4()); const [userName, setUserName] = useState(""); const [userImage, setUserImage] = useState(""); + const [userStatus, setUserStatus] = useState(""); useEffect(() => { const loadUserData = async () => { @@ -56,12 +57,13 @@ const Index = () => {