Compare commits
13 Commits
old-master
...
master
Author | SHA1 | Date | |
---|---|---|---|
e102d1ccb7 | |||
2b4f13cdb7 | |||
bd8d1fb31e | |||
294ee04b34 | |||
ebaef46456 | |||
eb1309f163 | |||
299bab4e6a | |||
356f604323 | |||
2ba1bc0658 | |||
77fa3417ef | |||
7671fc9787 | |||
bf22da655f | |||
6eff2b311f |
@ -4,9 +4,6 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -10,7 +10,7 @@ import themes from '@/assets/themes'
|
|||||||
import log from "@/util/log"
|
import log from "@/util/log"
|
||||||
|
|
||||||
// Prevent the splash screen from auto-hiding before asset loading is complete.
|
// Prevent the splash screen from auto-hiding before asset loading is complete.
|
||||||
SplashScreen.preventAutoHideAsync();
|
void SplashScreen.preventAutoHideAsync();
|
||||||
|
|
||||||
export default function RootLayout() {
|
export default function RootLayout() {
|
||||||
return (
|
return (
|
||||||
@ -34,7 +34,7 @@ function InnerRootLayout() {
|
|||||||
});
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (loaded) {
|
if (loaded) {
|
||||||
SplashScreen.hideAsync();
|
void SplashScreen.hideAsync();
|
||||||
}
|
}
|
||||||
}, [loaded]);
|
}, [loaded]);
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import themes from '@/assets/themes'
|
|||||||
import log from "@/util/log"
|
import log from "@/util/log"
|
||||||
|
|
||||||
// Prevent the splash screen from auto-hiding before asset loading is complete.
|
// Prevent the splash screen from auto-hiding before asset loading is complete.
|
||||||
SplashScreen.preventAutoHideAsync();
|
void SplashScreen.preventAutoHideAsync();
|
||||||
|
|
||||||
export default function RootLayout() {
|
export default function RootLayout() {
|
||||||
return (
|
return (
|
||||||
@ -34,7 +34,7 @@ function InnerRootLayout() {
|
|||||||
});
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (loaded) {
|
if (loaded) {
|
||||||
SplashScreen.hideAsync();
|
void SplashScreen.hideAsync();
|
||||||
}
|
}
|
||||||
}, [loaded]);
|
}, [loaded]);
|
||||||
|
|
||||||
|
@ -63,7 +63,9 @@ const Index = () => {
|
|||||||
setChanged={setUserDataChanged}
|
setChanged={setUserDataChanged}
|
||||||
onClose={() => setProfileActive(false)}
|
onClose={() => setProfileActive(false)}
|
||||||
/>
|
/>
|
||||||
<BottomNav toggleLocation={() => setProfileActive(true)}/>
|
<BottomNav
|
||||||
|
isProfileActive={isProfileActive}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
BIN
assets/images/broken.png
Normal file
BIN
assets/images/broken.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1022 KiB |
@ -3,7 +3,15 @@ import {StyleSheet} from "react-native";
|
|||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
//StatusPage
|
//StatusPage
|
||||||
container: { flex: 1, alignItems: "stretch" },
|
container: { flex: 1, alignItems: "stretch" },
|
||||||
listContainer: { flex: 1, width: "100%", backgroundColor: 'transparent' },
|
listContainer: {
|
||||||
|
flex: 1,
|
||||||
|
width: "100%",
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
//alignItems: "flex-start", // Aligns subheaders properly
|
||||||
|
paddingHorizontal: 10, // Adds some spacing
|
||||||
|
},
|
||||||
listSubheader: {
|
listSubheader: {
|
||||||
fontFamily: "Medium",
|
fontFamily: "Medium",
|
||||||
fontSize: 18, // Larger text
|
fontSize: 18, // Larger text
|
||||||
@ -12,20 +20,16 @@ const styles = StyleSheet.create({
|
|||||||
marginBottom: 10, // Add spacing below
|
marginBottom: 10, // Add spacing below
|
||||||
zIndex: 0,
|
zIndex: 0,
|
||||||
},
|
},
|
||||||
listWrapper: { flex: 1, padding: 5 },
|
//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, zIndex: 1},
|
listColumn: { flex: 1, paddingHorizontal: 5, zIndex: 1},
|
||||||
buttonContainer: {
|
buttonContainer: {
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
alignSelf: "stretch",
|
alignSelf: "stretch",
|
||||||
|
verticalAlign: "bottom",
|
||||||
paddingHorizontal: 10,
|
paddingHorizontal: 10,
|
||||||
paddingBottom: 20,
|
paddingBottom: 20,
|
||||||
|
marginHorizontal: 5,
|
||||||
},
|
},
|
||||||
actionButton: {
|
actionButton: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
@ -1,24 +1,46 @@
|
|||||||
import {Appbar, Button, Text, useTheme} from "react-native-paper";
|
import {Appbar, Portal, Button, Dialog, Text, useTheme } from "react-native-paper";
|
||||||
import {View} from "react-native";
|
import { View } from "react-native";
|
||||||
import styles from "@/assets/styles";
|
import styles from "@/assets/styles";
|
||||||
import React from "react";
|
import React, {useState} from "react";
|
||||||
|
import Location from "@/components/Location";
|
||||||
|
import Broken from "@/components/Broken";
|
||||||
|
|
||||||
const BottomNav = ({ toggleLocation }: { toggleLocation: () => void; }) => {
|
|
||||||
|
interface BNProps {
|
||||||
|
isProfileActive: boolean;
|
||||||
|
}
|
||||||
|
const BottomNav: React.FC<BNProps> = ({ isProfileActive }) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const [menuVisible, setMenuVisible] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{ backgroundColor: theme.colors.background }}>
|
<View style={ isProfileActive && { display: 'none' }}>
|
||||||
<Appbar.Header style={[styles.bottomBar, { backgroundColor: theme.colors.primaryContainer }]} >
|
<View style={{ backgroundColor: theme.colors.background }}>
|
||||||
<View style={{ alignItems: "center", flexDirection: "row", justifyContent: "space-between", padding: 10, flex: 1, paddingHorizontal: 15 }}>
|
<Appbar.Header style={[styles.bottomBar, { backgroundColor: theme.colors.primaryContainer }]} >
|
||||||
<Text style={{ color: theme.colors.primary, fontFamily: "SpaceReg" }}>Daisy Knight Dog Park</Text>
|
<View style={{ alignItems: "center", flexDirection: "row", justifyContent: "space-between", padding: 10, flex: 1, paddingHorizontal: 15 }}>
|
||||||
<Button
|
<Text style={{ color: theme.colors.primary, fontFamily: "SpaceReg" }}>Daisy Knight Dog Park</Text>
|
||||||
mode="text"
|
<Button
|
||||||
onPress={() => toggleLocation }
|
mode="text"
|
||||||
style={{ backgroundColor: theme.colors.primary, height: styles.bottomBar.height/2, justifyContent: "center"}}
|
onPress={() => setMenuVisible(true) }
|
||||||
labelStyle={{ color: theme.colors.onPrimary, fontFamily: "SpaceReg"}}>
|
style={{ backgroundColor: theme.colors.primary, height: styles.bottomBar.height/2, justifyContent: "center"}}
|
||||||
Change
|
labelStyle={{ color: theme.colors.onPrimary, fontFamily: "SpaceReg"}}>
|
||||||
</Button>
|
Change
|
||||||
</View>
|
</Button>
|
||||||
</Appbar.Header>
|
</View>
|
||||||
|
</Appbar.Header>
|
||||||
|
<Portal>
|
||||||
|
<Dialog visible={menuVisible} onDismiss={() => setMenuVisible(false)} style={{ backgroundColor: theme.colors.primaryContainer, maxHeight: 400 }}>
|
||||||
|
<Dialog.Title style={{ color: theme.colors.primary, textAlign: 'center' }}>Location</Dialog.Title>
|
||||||
|
<Broken />
|
||||||
|
<Dialog.Actions style={{ justifyContent: "center" }}>
|
||||||
|
<Button onPress={() => setMenuVisible(false)} mode="contained" style={{ backgroundColor: theme.colors.inversePrimary }} labelStyle={{ color: theme.colors.primary }}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</Dialog.Actions>
|
||||||
|
</Dialog>
|
||||||
|
</Portal>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
21
components/Broken.tsx
Normal file
21
components/Broken.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import {Title, useTheme} from "react-native-paper";
|
||||||
|
import { Image, View } from "react-native";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const Broken = () => {
|
||||||
|
const theme = useTheme();
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<Title style={{color: theme.colors.onBackground, fontSize: 16, textAlign: 'center', fontFamily: "Light"}}>The Internet is a Series of Tubes</Title>
|
||||||
|
<Image
|
||||||
|
source={require("../assets/images/broken.png")}
|
||||||
|
style={{ alignSelf: 'center', resizeMode: "contain", height: 400/3 }}
|
||||||
|
/>
|
||||||
|
<Title style={{color: theme.colors.onBackground, fontSize: 16, textAlign: 'center', fontFamily: "Light"}}>And these aren't connected. {"\n"} (We're still working on this part.)</Title>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Broken;
|
@ -1,23 +1,9 @@
|
|||||||
import { Dialog, Portal, useTheme } from "react-native-paper";
|
import React from "react";
|
||||||
import React, {useEffect} from "react";
|
import Broken from "@/components/Broken";
|
||||||
|
|
||||||
interface LocationProps {
|
const Location = () => {
|
||||||
visible: boolean;
|
|
||||||
}
|
|
||||||
const Location: React.FC<LocationProps> = ({ visible }) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (visible) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}, [visible]);
|
|
||||||
return (
|
return (
|
||||||
<Portal>
|
<Broken />
|
||||||
<Dialog visible={visible} >
|
|
||||||
<Dialog.Title style={{ color: theme.colors.onBackground, textAlign: 'center', fontFamily: "Light"}}>Choose Your Location</Dialog.Title>
|
|
||||||
</Dialog>
|
|
||||||
</Portal>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
88
components/Privacy.tsx
Normal file
88
components/Privacy.tsx
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { ScrollView } from 'react-native';
|
||||||
|
import { Card, Title, Paragraph, Text, Dialog } from 'react-native-paper';
|
||||||
|
|
||||||
|
const Privacy = () => {
|
||||||
|
return (
|
||||||
|
<Dialog.Content style={{ maxHeight: 300 }}>
|
||||||
|
<ScrollView style={{ padding: 16 }}>
|
||||||
|
<Card style={{ marginBottom: 16 }}>
|
||||||
|
<Card.Content>
|
||||||
|
<Title style={{ fontSize: 20, marginBottom: 8 }}>Privacy Policy</Title>
|
||||||
|
<Paragraph style={{ fontSize: 16 }}>
|
||||||
|
<Text style={{ fontStyle: 'italic' }}>Effective Date: 4/1/25</Text>
|
||||||
|
</Paragraph>
|
||||||
|
<Paragraph style={{ fontSize: 16 }}>
|
||||||
|
Thank you for using <Text style={{ fontWeight: 'bold' }}>Pogdark</Text> ("we", "our", or "us"). We are committed to protecting your privacy. This Privacy Policy explains how we handle your information when you use our application.
|
||||||
|
</Paragraph>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card style={{ marginBottom: 16 }}>
|
||||||
|
<Card.Content>
|
||||||
|
<Title style={{ fontSize: 20, marginBottom: 8 }}>1. No Tracking</Title>
|
||||||
|
<Paragraph style={{ fontSize: 16 }}>
|
||||||
|
We do <Text style={{ fontWeight: 'bold' }}>not</Text> collect or track any personal information. We do <Text style={{ fontWeight: 'bold' }}>not</Text> use analytics tools, cookies, or any third-party tracking services.
|
||||||
|
</Paragraph>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card style={{ marginBottom: 16 }}>
|
||||||
|
<Card.Content>
|
||||||
|
<Title style={{ fontSize: 20, marginBottom: 8 }}>2. Ephemeral Data Storage</Title>
|
||||||
|
<Paragraph style={{ fontSize: 16 }}>
|
||||||
|
Any data entered or generated within the app is stored only <Text style={{ fontWeight: 'bold' }}>temporarily</Text> and exists only for the duration of your session. Once the app is closed or the session ends, all data is deleted automatically. We do not retain any user data on our servers or devices beyond the current session.
|
||||||
|
</Paragraph>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card style={{ marginBottom: 16 }}>
|
||||||
|
<Card.Content>
|
||||||
|
<Title style={{ fontSize: 20, marginBottom: 8 }}>3. No Account or Registration Required</Title>
|
||||||
|
<Paragraph style={{ fontSize: 16 }}>
|
||||||
|
You are not required to create an account or provide any personal details (such as name, email address, or phone number) to use the app.
|
||||||
|
</Paragraph>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card style={{ marginBottom: 16 }}>
|
||||||
|
<Card.Content>
|
||||||
|
<Title style={{ fontSize: 20, marginBottom: 8 }}>4. No Data Sharing</Title>
|
||||||
|
<Paragraph style={{ fontSize: 16 }}>
|
||||||
|
Since we do not collect data, we do not share any data with third parties.
|
||||||
|
</Paragraph>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card style={{ marginBottom: 16 }}>
|
||||||
|
<Card.Content>
|
||||||
|
<Title style={{ fontSize: 20, marginBottom: 8 }}>5. Security</Title>
|
||||||
|
<Paragraph style={{ fontSize: 16 }}>
|
||||||
|
While we don’t store personal data, we still take standard precautions to secure ephemeral information during your use of the app.
|
||||||
|
</Paragraph>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card style={{ marginBottom: 16 }}>
|
||||||
|
<Card.Content>
|
||||||
|
<Title style={{ fontSize: 20, marginBottom: 8 }}>6. Changes to This Privacy Policy</Title>
|
||||||
|
<Paragraph style={{ fontSize: 16 }}>
|
||||||
|
If we ever make changes to this policy, we will update the date at the top and clearly communicate the changes within the app. However, any future version will always respect your privacy.
|
||||||
|
</Paragraph>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<Card.Content>
|
||||||
|
<Title style={{ fontSize: 20, marginBottom: 8 }}>7. Contact</Title>
|
||||||
|
<Paragraph style={{ fontSize: 16 }}>
|
||||||
|
If you have any questions or concerns about this policy, feel free to contact us at <Text style={{ fontStyle: 'italic' }}>support@pogdark.com</Text>.
|
||||||
|
</Paragraph>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
</ScrollView>
|
||||||
|
</Dialog.Content>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Privacy;
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useState, useRef } 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 {Animated, Easing, ImageBackground, useColorScheme, View} from "react-native";
|
import {Animated, Easing, ImageBackground, ScrollView, useColorScheme, View} from "react-native";
|
||||||
import { Avatar, List, Button, useTheme, } from "react-native-paper";
|
import { Avatar, List, Button, useTheme, } from "react-native-paper";
|
||||||
import themes from "@/assets/themes";
|
import themes from "@/assets/themes";
|
||||||
import styles from "@/assets/styles";
|
import styles from "@/assets/styles";
|
||||||
@ -167,76 +167,66 @@ const Status: React.FC<StatusProps> = ({ id, name, image, currentStatus, setStat
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, isProfileActive && { display: 'none' }]}>
|
<View style={[styles.container, isProfileActive && { display: 'none' }]}>
|
||||||
<View style={styles.listContainer}>
|
|
||||||
<View style={styles.listRow}>
|
|
||||||
<View style={styles.listColumn}>
|
|
||||||
<List.Section>
|
|
||||||
<List.Subheader style={[styles.listSubheader, { color: theme.colors.primary }]}>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: themes[item.Theme as keyof typeof themes][colorScheme === 'dark' ? 'dark' : 'light'].colors.primaryContainer }]}>
|
|
||||||
<List.Item
|
|
||||||
key={item.Id}
|
|
||||||
title={item.Name}
|
|
||||||
titleStyle={{ color: themes[item.Theme as keyof typeof themes][colorScheme === 'dark' ? 'dark' : 'light'].colors.primary, fontFamily: "SpaceBold" }}
|
|
||||||
description={new Date(item.Timestamp).toLocaleTimeString([], { hour: 'numeric', minute: '2-digit', hour12: true })}
|
|
||||||
descriptionStyle={{ color: themes[item.Theme as keyof typeof themes][colorScheme === 'dark' ? 'dark' : 'light'].colors.primary, fontFamily: "SpaceReg" }}
|
|
||||||
left={(props) => <Avatar.Image {...props} size={50} 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.primary }]}>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: themes[item.Theme as keyof typeof themes][colorScheme === 'dark' ? 'dark' : 'light'].colors.primaryContainer }]}>
|
|
||||||
<List.Item
|
|
||||||
key={item.Id}
|
|
||||||
title={item.Name}
|
|
||||||
titleStyle={{ color: themes[item.Theme as keyof typeof themes][colorScheme === 'dark' ? 'dark' : 'light'].colors.primary, fontFamily: "SpaceBold" }}
|
|
||||||
description={new Date(item.Timestamp).toLocaleTimeString([], { hour: 'numeric', minute: '2-digit', hour12: true })}
|
|
||||||
descriptionStyle={{ color: themes[item.Theme as keyof typeof themes][colorScheme === 'dark' ? 'dark' : 'light'].colors.primary, fontFamily: "SpaceReg" }}
|
|
||||||
left={(props) => <Avatar.Image {...props} size={50} source={{ uri: `data:image/png;base64,${item.Image}` }} />}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
))}
|
|
||||||
</List.Section>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<ImageBackground source={require('../assets/images/bg.webp')} style={styles.imageBackground} />
|
<ImageBackground source={require('../assets/images/bg.webp')} style={styles.imageBackground} />
|
||||||
|
<View style={styles.listContainer}>
|
||||||
|
<List.Section style={styles.listColumn}>
|
||||||
|
<List.Subheader style={[styles.listSubheader, { color: theme.colors.primary }]}>On the Way</List.Subheader>
|
||||||
|
<ScrollView>
|
||||||
|
{messages.filter(msg => msg.Status === "On the Way")
|
||||||
|
.sort((a, b) => new Date(a.Timestamp).getTime() - new Date(b.Timestamp).getTime())
|
||||||
|
.map(item => (
|
||||||
|
<List.Item
|
||||||
|
key={item.Id}
|
||||||
|
title={item.Name}
|
||||||
|
titleStyle={{ color: themes[item.Theme as keyof typeof themes][colorScheme === 'dark' ? 'dark' : 'light'].colors.primary, fontFamily: "SpaceBold" }}
|
||||||
|
style={[styles.card, { backgroundColor: themes[item.Theme as keyof typeof themes][colorScheme === 'dark' ? 'dark' : 'light'].colors.primaryContainer }]}
|
||||||
|
description={new Date(item.Timestamp).toLocaleTimeString([], { hour: 'numeric', minute: '2-digit', hour12: true })}
|
||||||
|
descriptionStyle={{ color: themes[item.Theme as keyof typeof themes][colorScheme === 'dark' ? 'dark' : 'light'].colors.primary, fontFamily: "SpaceReg" }}
|
||||||
|
left={(props) => <Avatar.Image {...props} size={50} source={{ uri: `data:image/png;base64,${item.Image}` }} />}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ScrollView>
|
||||||
|
</List.Section>
|
||||||
|
<List.Section style={styles.listColumn}>
|
||||||
|
<List.Subheader style={[styles.listSubheader, { color: theme.colors.primary }]}>Arrived</List.Subheader>
|
||||||
|
<ScrollView>
|
||||||
|
{messages.filter(msg => msg.Status === "Arrived")
|
||||||
|
.sort((a, b) => new Date(a.Timestamp).getTime() - new Date(b.Timestamp).getTime())
|
||||||
|
.map(item => (
|
||||||
|
<List.Item
|
||||||
|
key={item.Id}
|
||||||
|
title={item.Name}
|
||||||
|
titleStyle={{ color: themes[item.Theme as keyof typeof themes][colorScheme === 'dark' ? 'dark' : 'light'].colors.primary, fontFamily: "SpaceBold" }}
|
||||||
|
style={[styles.card, { backgroundColor: themes[item.Theme as keyof typeof themes][colorScheme === 'dark' ? 'dark' : 'light'].colors.primaryContainer }]}
|
||||||
|
description={new Date(item.Timestamp).toLocaleTimeString([], { hour: 'numeric', minute: '2-digit', hour12: true })}
|
||||||
|
descriptionStyle={{ color: themes[item.Theme as keyof typeof themes][colorScheme === 'dark' ? 'dark' : 'light'].colors.primary, fontFamily: "SpaceReg" }}
|
||||||
|
left={(props) => <Avatar.Image {...props} size={50} source={{ uri: `data:image/png;base64,${item.Image}` }} />}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ScrollView>
|
||||||
|
</List.Section>
|
||||||
|
</View>
|
||||||
<View style={styles.buttonContainer}>
|
<View style={styles.buttonContainer}>
|
||||||
<Animated.View style={{ flex: 1 }}>
|
<Button
|
||||||
<Button
|
mode="contained"
|
||||||
mode="contained"
|
onPress={() => handleStatusPress("On the Way")}
|
||||||
onPress={() => handleStatusPress("On the Way")}
|
style={[
|
||||||
style={[
|
styles.actionButton,
|
||||||
styles.actionButton,
|
{ backgroundColor: currentStatus === "On the Way" ? pulseColorOnTheWay : theme.colors.primaryContainer }
|
||||||
{ backgroundColor: currentStatus === "On the Way" ? pulseColorOnTheWay : theme.colors.primaryContainer }
|
]}
|
||||||
]}
|
labelStyle={{ color: theme.colors.primary, fontFamily: "Medium", fontSize: 16 }}>
|
||||||
labelStyle={{ color: theme.colors.primary, fontFamily: "Medium", fontSize: 16 }}>
|
{getButtonLabel("On the Way")}
|
||||||
{getButtonLabel("On the Way")}
|
</Button>
|
||||||
</Button>
|
<Button
|
||||||
</Animated.View>
|
mode="contained"
|
||||||
<Animated.View style={{ flex: 1 }}>
|
onPress={() => handleStatusPress("Arrived")}
|
||||||
<Button
|
style={[
|
||||||
mode="contained"
|
styles.actionButton,
|
||||||
onPress={() => handleStatusPress("Arrived")}
|
{ backgroundColor: currentStatus === "Arrived" ? pulseColorArrived : theme.colors.primaryContainer }
|
||||||
style={[
|
]}
|
||||||
styles.actionButton,
|
labelStyle={{ color: theme.colors.primary, fontFamily: "Medium", fontSize: 16 }}>
|
||||||
{ backgroundColor: currentStatus === "Arrived" ? pulseColorArrived : theme.colors.primaryContainer }
|
{getButtonLabel("Arrived")}
|
||||||
]}
|
</Button>
|
||||||
labelStyle={{ color: theme.colors.primary, fontFamily: "Medium", fontSize: 16 }}>
|
|
||||||
{getButtonLabel("Arrived")}
|
|
||||||
</Button>
|
|
||||||
</Animated.View>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -3,11 +3,15 @@ import {Image, useColorScheme, View} from "react-native";
|
|||||||
import React, {useState} from "react";
|
import React, {useState} from "react";
|
||||||
import styles from "@/assets/styles";
|
import styles from "@/assets/styles";
|
||||||
import About from "@/components/About";
|
import About from "@/components/About";
|
||||||
|
import Privacy from "@/components/Privacy";
|
||||||
|
import Broken from "@/components/Broken";
|
||||||
|
|
||||||
const TopNav = ({ toggleProfile }: { toggleProfile: () => void; }) => {
|
const TopNav = ({ toggleProfile }: { toggleProfile: () => void; }) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const colorScheme = useColorScheme();
|
const colorScheme = useColorScheme();
|
||||||
const [aboutVisible, setAboutVisible] = useState(false);
|
const [aboutVisible, setAboutVisible] = useState(false);
|
||||||
|
const [privacyVisible, setPrivacyVisible] = useState(false);
|
||||||
|
const [bugVisible, setBugVisible] = useState(false);
|
||||||
const [menuVisible, setMenuVisible] = useState(false);
|
const [menuVisible, setMenuVisible] = useState(false);
|
||||||
const [menuAnchor, setMenuAnchor] = useState<{ x: number; y: number } | null>(null);
|
const [menuAnchor, setMenuAnchor] = useState<{ x: number; y: number } | null>(null);
|
||||||
|
|
||||||
@ -17,6 +21,8 @@ const TopNav = ({ toggleProfile }: { toggleProfile: () => void; }) => {
|
|||||||
<View>
|
<View>
|
||||||
<Menu visible={menuVisible} onDismiss={() => setMenuVisible(false)} anchor={menuAnchor} style={{ backgroundColor: theme.colors.primaryContainer }}>
|
<Menu visible={menuVisible} onDismiss={() => setMenuVisible(false)} anchor={menuAnchor} style={{ backgroundColor: theme.colors.primaryContainer }}>
|
||||||
<Menu.Item onPress={() => { setMenuVisible(false); setAboutVisible(true);}} title="About Us" style={{ backgroundColor: theme.colors.primaryContainer }}/>
|
<Menu.Item onPress={() => { setMenuVisible(false); setAboutVisible(true);}} title="About Us" style={{ backgroundColor: theme.colors.primaryContainer }}/>
|
||||||
|
<Menu.Item onPress={() => { setMenuVisible(false); setPrivacyVisible(true);}} title="Privacy Policy" style={{ backgroundColor: theme.colors.primaryContainer }}/>
|
||||||
|
<Menu.Item onPress={() => { setMenuVisible(false); setBugVisible(true);}} title="Report a Bug" style={{ backgroundColor: theme.colors.primaryContainer }}/>
|
||||||
</Menu>
|
</Menu>
|
||||||
<Appbar.Action icon="menu"
|
<Appbar.Action icon="menu"
|
||||||
onPressIn={(event) => {
|
onPressIn={(event) => {
|
||||||
@ -43,6 +49,24 @@ const TopNav = ({ toggleProfile }: { toggleProfile: () => void; }) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Dialog.Actions>
|
</Dialog.Actions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
<Dialog visible={privacyVisible} onDismiss={() => setPrivacyVisible(false)} style={{ backgroundColor: theme.colors.primaryContainer }}>
|
||||||
|
<Dialog.Title style={{ color: theme.colors.primary, textAlign: 'center' }}>Privacy Policy</Dialog.Title>
|
||||||
|
<Privacy />
|
||||||
|
<Dialog.Actions style={{ justifyContent: "center" }}>
|
||||||
|
<Button onPress={() => setPrivacyVisible(false)} mode="contained" style={{ backgroundColor: theme.colors.inversePrimary }} labelStyle={{ color: theme.colors.primary }}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</Dialog.Actions>
|
||||||
|
</Dialog>
|
||||||
|
<Dialog visible={bugVisible} onDismiss={() => setBugVisible(false)} style={{ backgroundColor: theme.colors.primaryContainer }}>
|
||||||
|
<Dialog.Title style={{ color: theme.colors.primary, textAlign: 'center' }}>Report A Bug</Dialog.Title>
|
||||||
|
<Broken />
|
||||||
|
<Dialog.Actions style={{ justifyContent: "center" }}>
|
||||||
|
<Button onPress={() => setBugVisible(false)} mode="contained" style={{ backgroundColor: theme.colors.inversePrimary }} labelStyle={{ color: theme.colors.primary }}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</Dialog.Actions>
|
||||||
|
</Dialog>
|
||||||
</Portal>
|
</Portal>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -71,7 +71,7 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loadUserData();
|
void loadUserData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -90,14 +90,14 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
saveUserData();
|
void saveUserData();
|
||||||
}, [userDataChanged]);
|
}, [userDataChanged]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleAppStateChange = (nextAppState: string) => {
|
const handleAppStateChange = (nextAppState: string) => {
|
||||||
if (appState.match(/inactive|background/) && nextAppState === "active") {
|
if (appState.match(/inactive|background/) && nextAppState === "active") {
|
||||||
if (!isLoading) {
|
if (!isLoading) {
|
||||||
fetchCurrentStatus();
|
void fetchCurrentStatus();
|
||||||
} else {
|
} else {
|
||||||
log.debug("Waiting for loading to complete before fetching status...");
|
log.debug("Waiting for loading to complete before fetching status...");
|
||||||
}
|
}
|
||||||
|
8
package-lock.json
generated
8
package-lock.json
generated
@ -13,7 +13,7 @@
|
|||||||
"@react-native-async-storage/async-storage": "~2.1.1",
|
"@react-native-async-storage/async-storage": "~2.1.1",
|
||||||
"@react-navigation/bottom-tabs": "^7.2.0",
|
"@react-navigation/bottom-tabs": "^7.2.0",
|
||||||
"@react-navigation/native": "^7.0.14",
|
"@react-navigation/native": "^7.0.14",
|
||||||
"axios": "~1.7.9",
|
"axios": "~1.8.4",
|
||||||
"expo": "~52.0.25",
|
"expo": "~52.0.25",
|
||||||
"expo-blur": "~14.0.2",
|
"expo-blur": "~14.0.2",
|
||||||
"expo-constants": "~17.0.4",
|
"expo-constants": "~17.0.4",
|
||||||
@ -5197,9 +5197,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.7.9",
|
"version": "1.8.4",
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz",
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
|
||||||
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
|
"integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.6",
|
"follow-redirects": "^1.15.6",
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
"react-use-websocket": "~4.13.0",
|
"react-use-websocket": "~4.13.0",
|
||||||
"@react-native-async-storage/async-storage": "~2.1.1",
|
"@react-native-async-storage/async-storage": "~2.1.1",
|
||||||
"expo-image-picker": "~16.0.6",
|
"expo-image-picker": "~16.0.6",
|
||||||
"axios": "~1.7.9",
|
"axios": "~1.8.4",
|
||||||
"uuid": "~11.0.5",
|
"uuid": "~11.0.5",
|
||||||
"react-native-paper": "~5.13.1",
|
"react-native-paper": "~5.13.1",
|
||||||
"react-native-vector-icons": "~10.2.0",
|
"react-native-vector-icons": "~10.2.0",
|
||||||
|
Loading…
Reference in New Issue
Block a user