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
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:
parent
7cf7d06b3e
commit
839ed0b340
12
app.config.ts
Normal file
12
app.config.ts
Normal 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,
|
||||||
|
},
|
||||||
|
};
|
4
app.json
4
app.json
@ -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",
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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
BIN
assets/images/bg.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 552 KiB |
19
package-lock.json
generated
19
package-lock.json
generated
@ -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",
|
||||||
|
@ -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": {
|
||||||
|
@ -10,8 +10,6 @@
|
|||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.ts",
|
"**/*.ts",
|
||||||
"**/*.tsx",
|
"**/*.tsx"
|
||||||
".expo/types/**/*.ts",
|
|
||||||
"expo-env.d.ts"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user