Adding .env, background, statuspage updates
Some checks failed
Build Flutter Web and Docker Image for Local Registry / Build React Native Web App (push) Failing after 5s

This commit is contained in:
whysman 2025-02-21 12:27:59 -05:00
parent 7cf7d06b3e
commit 839ed0b340
8 changed files with 158 additions and 29 deletions

12
app.config.ts Normal file
View File

@ -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,
},
};

View File

@ -1,7 +1,7 @@
{ {
"expo": { "expo": {
"name": "pogdark_app_rn", "name": "Pogdark",
"slug": "pogdark_app_rn", "slug": "Pogdark",
"version": "1.0.0", "version": "1.0.0",
"orientation": "portrait", "orientation": "portrait",
"icon": "./assets/images/icon.png", "icon": "./assets/images/icon.png",

View File

@ -1,9 +1,12 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState, useRef } from "react";
import useWebSocket from "react-use-websocket"; import useWebSocket from "react-use-websocket";
import axios from "axios"; 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"; 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 { interface Message {
Id: string; Id: string;
Name: string; Name: string;
@ -14,12 +17,13 @@ interface Message {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { flex: 1, alignItems: "stretch" }, container: { flex: 1, alignItems: "stretch" },
listContainer: { flex: 1, width: "100%" }, listContainer: { flex: 1, width: "100%", backgroundColor: 'transparent' },
listSubheader: { listSubheader: {
fontSize: 18, // Larger text fontSize: 18, // Larger text
textAlign: "center", // Center the text textAlign: "center", // Center the text
fontWeight: "bold", // Make it more distinct fontWeight: "bold", // Make it more distinct
marginBottom: 10, // Add spacing below marginBottom: 10, // Add spacing below
zIndex: 0,
}, },
listWrapper: { flex: 1, padding: 5 }, listWrapper: { flex: 1, padding: 5 },
listRow: { listRow: {
@ -28,7 +32,7 @@ const styles = StyleSheet.create({
alignItems: "flex-start", // Aligns subheaders properly alignItems: "flex-start", // Aligns subheaders properly
paddingHorizontal: 10, // Adds some spacing paddingHorizontal: 10, // Adds some spacing
}, },
listColumn: { flex: 1, paddingHorizontal: 5 }, listColumn: { flex: 1, paddingHorizontal: 5, zIndex: 1},
buttonContainer: { buttonContainer: {
flexDirection: "row", flexDirection: "row",
justifyContent: "space-between", justifyContent: "space-between",
@ -47,24 +51,97 @@ const styles = StyleSheet.create({
shadowOffset: { width: 0, height: 2 }, shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2, shadowOpacity: 0.2,
shadowRadius: 4, 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 }) => { interface StatusProps {
const theme = useTheme(); id: string;
name: string;
image: string;
currentStatus: string;
setStatus: (currentStatus: string) => void;
isProfileActive: boolean;
}
const StatusPage: React.FC<StatusProps> = ({ id, name, image, currentStatus, setStatus, isProfileActive }) => {
const theme = useTheme();
const [messages, setMessages] = useState<Message[]>([]); const [messages, setMessages] = useState<Message[]>([]);
const { lastMessage } = useWebSocket("ws://localhost:8080/ws", { const { lastMessage } = useWebSocket(WS_URL + "/ws", {
shouldReconnect: () => true, 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(() => { useEffect(() => {
if (lastMessage?.data) { if (lastMessage?.data) {
try { try {
const newMessage: Message = JSON.parse(lastMessage.data); const newMessage: Message = JSON.parse(lastMessage.data);
setMessages((prev) => { setMessages((prev) => {
if (newMessage.Status === "removed") { 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);
} }
return prev.filter((msg) => msg.Id !== newMessage.Id).concat(newMessage); 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); console.error("Error parsing WebSocket message:", error);
} }
} }
}, [lastMessage]); }, [lastMessage, setStatus, id]);
const sendStatus = async (status: string) => { const sendStatus = async (status: string) => {
try { try {
const message: Message = { Id: id, Name: name, Image: image, Status: status, Timestamp: new Date().toISOString() }; 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) { } catch (error) {
console.error("Error sending status:", error); console.error("Error sending status:", error);
} }
@ -86,7 +164,7 @@ const StatusPage = ({ id, name, image, isProfileActive }: { id: string; name: st
return ( return (
<View style={[styles.container, { backgroundColor: theme.colors.background }, isProfileActive && { display: 'none' }]}> <View style={[styles.container, { backgroundColor: theme.colors.background }, isProfileActive && { display: 'none' }]}>
<View style={[styles.listContainer, { backgroundColor: theme.colors.surface }]}> <View style={styles.listContainer}>
<View style={styles.listRow}> <View style={styles.listRow}>
<View style={styles.listColumn}> <View style={styles.listColumn}>
<List.Section> <List.Section>
@ -128,17 +206,34 @@ const StatusPage = ({ id, name, image, isProfileActive }: { id: string; name: st
</View> </View>
</View> </View>
</View> </View>
<ImageBackground source={require('../assets/images/bg.webp')} style={styles.imageBackground} />
<View style={styles.buttonContainer}> <View style={styles.buttonContainer}>
<Button mode="contained" onPress={() => sendStatus("On the Way")} <Animated.View style={{ flex: 1 }}>
style={[styles.actionButton, { backgroundColor: theme.colors.primary }]} <Button
labelStyle={{ color: theme.colors.onPrimary }}>On the Way</Button> mode="contained"
<Button mode="contained" onPress={() => sendStatus("Arrived")} onPress={() => sendStatus("On the Way")}
style={[styles.actionButton, { backgroundColor: theme.colors.primary }]} style={[
labelStyle={{ color: theme.colors.onPrimary }} >Arrived</Button> styles.actionButton,
{ backgroundColor: currentStatus === "On the Way" ? pulseColorOnTheWay : theme.colors.primary }
]}
labelStyle={{ color: theme.colors.onPrimary }}>
On the Way
</Button>
</Animated.View>
<Animated.View style={{ flex: 1 }}>
<Button
mode="contained"
onPress={() => sendStatus("Arrived")}
style={[
styles.actionButton,
{ backgroundColor: currentStatus === "Arrived" ? pulseColorArrived : theme.colors.primary }
]}
labelStyle={{ color: theme.colors.onPrimary }}>
Arrived
</Button>
</Animated.View>
</View> </View>
</View> </View>
); );
}; };

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react'; 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 { useTheme } from "react-native-paper";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import AsyncStorage from "@react-native-async-storage/async-storage"; import AsyncStorage from "@react-native-async-storage/async-storage";
@ -13,6 +13,7 @@ const Index = () => {
const [userId, setUserId] = useState(uuidv4()); const [userId, setUserId] = useState(uuidv4());
const [userName, setUserName] = useState(""); const [userName, setUserName] = useState("");
const [userImage, setUserImage] = useState(""); const [userImage, setUserImage] = useState("");
const [userStatus, setUserStatus] = useState("");
useEffect(() => { useEffect(() => {
const loadUserData = async () => { const loadUserData = async () => {
@ -56,12 +57,13 @@ const Index = () => {
<View style={[styles.container,{ backgroundColor: colors.background }]}> <View style={[styles.container,{ backgroundColor: colors.background }]}>
<Nav <Nav
toggleProfile={() => setProfileActive(true)} toggleProfile={() => setProfileActive(true)}
isProfileActive={isProfileActive}
/> />
<StatusPage <StatusPage
id={userId} id={userId}
name={userName} name={userName}
image={userImage} image={userImage}
currentStatus={userStatus}
setStatus={setUserStatus}
isProfileActive={isProfileActive} isProfileActive={isProfileActive}
/> />
<ProfileScreen <ProfileScreen
@ -79,6 +81,13 @@ const Index = () => {
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { flex: 1, alignItems: "stretch" }, container: { flex: 1, alignItems: "stretch" },
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.3, // Fades the image
},
}); });
export default Index; export default Index;

BIN
assets/images/bg.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 KiB

19
package-lock.json generated
View File

@ -1,11 +1,11 @@
{ {
"name": "pogdark_app_rn", "name": "Pogdark",
"version": "1.0.0", "version": "1.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "pogdark_app_rn", "name": "Pogdark",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@expo/metro-runtime": "~4.0.1", "@expo/metro-runtime": "~4.0.1",
@ -30,6 +30,7 @@
"react": "18.3.1", "react": "18.3.1",
"react-dom": "18.3.1", "react-dom": "18.3.1",
"react-native": "0.77.1", "react-native": "0.77.1",
"react-native-config": "~1.5.5",
"react-native-gesture-handler": "~2.20.2", "react-native-gesture-handler": "~2.20.2",
"react-native-paper": "~5.13.1", "react-native-paper": "~5.13.1",
"react-native-reanimated": "~3.16.1", "react-native-reanimated": "~3.16.1",
@ -12429,6 +12430,20 @@
} }
} }
}, },
"node_modules/react-native-config": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/react-native-config/-/react-native-config-1.5.5.tgz",
"integrity": "sha512-dGdLnBU0cd5xL5bF0ROTmHYbsstZnQKOEPfglvZi1vStvAjpld14X25K6mY3KGPTMWAzx6TbjKeq5dR+ILuMMA==",
"license": "MIT",
"peerDependencies": {
"react-native-windows": ">=0.61"
},
"peerDependenciesMeta": {
"react-native-windows": {
"optional": true
}
}
},
"node_modules/react-native-gesture-handler": { "node_modules/react-native-gesture-handler": {
"version": "2.20.2", "version": "2.20.2",
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.20.2.tgz", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.20.2.tgz",

View File

@ -1,5 +1,5 @@
{ {
"name": "pogdark_app_rn", "name": "Pogdark",
"main": "expo-router/entry", "main": "expo-router/entry",
"version": "1.0.0", "version": "1.0.0",
"scripts": { "scripts": {

View File

@ -10,8 +10,6 @@
}, },
"include": [ "include": [
"**/*.ts", "**/*.ts",
"**/*.tsx", "**/*.tsx"
".expo/types/**/*.ts",
"expo-env.d.ts"
] ]
} }