Color & storage fixes. Readme update
All checks were successful
Build Flutter Web and Docker Image for Local Registry / Build React Native Web App (push) Successful in 10m32s
All checks were successful
Build Flutter Web and Docker Image for Local Registry / Build React Native Web App (push) Successful in 10m32s
This commit is contained in:
parent
4abd732fe8
commit
d4d8d34d1f
@ -13,3 +13,11 @@
|
||||
```bash
|
||||
npm run web
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Currently we're not looking for contributors.
|
||||
|
||||
## License
|
||||
|
||||
This work is licensed under [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/)
|
||||
|
@ -15,7 +15,7 @@ const Nav = ({ toggleProfile }: { toggleProfile: () => void; }) => {
|
||||
|
||||
return (
|
||||
<View style={{ backgroundColor: theme.colors.background }}>
|
||||
<Appbar.Header style={{ backgroundColor: theme.colors.primary }}>
|
||||
<Appbar.Header style={{ backgroundColor: theme.colors.inversePrimary }}>
|
||||
<View>
|
||||
<Menu visible={menuVisible} onDismiss={() => setMenuVisible(false)} anchor={menuAnchor}>
|
||||
<Menu.Item onPress={() => { setMenuVisible(false); setAboutVisible(true);}} title="About Us"/>
|
||||
@ -25,12 +25,12 @@ const Nav = ({ toggleProfile }: { toggleProfile: () => void; }) => {
|
||||
setMenuAnchor({ x: event.nativeEvent.pageX, y: event.nativeEvent.pageY + 40 });
|
||||
setMenuVisible(true);
|
||||
}}
|
||||
iconColor={theme.colors.inversePrimary} />
|
||||
iconColor={theme.colors.primary} />
|
||||
</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.Action icon="pencil" onPress={toggleProfile} iconColor={ theme.colors.primary } />
|
||||
</Appbar.Header>
|
||||
<Portal>
|
||||
<Dialog visible={aboutVisible} onDismiss={() => setAboutVisible(false)} style={{ backgroundColor: theme.colors.background }}>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Platform } from "react-native";
|
||||
import { Button, TextInput, Dialog, Portal, Avatar, useTheme } from "react-native-paper";
|
||||
import { Asset } from 'expo-asset';
|
||||
@ -12,11 +12,24 @@ interface ProfileScreenProps {
|
||||
setName: (name: string) => void;
|
||||
image: string;
|
||||
setImage: (image: string) => void;
|
||||
setChanged: (dataChanged: boolean) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const ProfileScreen: React.FC<ProfileScreenProps> = ({ visible, name, setName, image, setImage, onClose }) => {
|
||||
const ProfileScreen: React.FC<ProfileScreenProps> = ({ visible, name, setName, image, setImage, setChanged, onClose }) => {
|
||||
const { colors } = useTheme();
|
||||
const isNameEmpty = !name.trim();
|
||||
|
||||
// Track the initial values when the component first mounts
|
||||
const [initialName, setInitialName] = useState(name);
|
||||
const [initialImage, setInitialImage] = useState(image);
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setInitialName(name); // Store initial name when the profile opens
|
||||
setInitialImage(image); // Store initial image when the profile opens
|
||||
}
|
||||
}, [visible]); // Reset when the dialog is opened
|
||||
|
||||
useEffect(() => {
|
||||
const loadDefaultImage = async () => {
|
||||
@ -45,23 +58,43 @@ const ProfileScreen: React.FC<ProfileScreenProps> = ({ visible, name, setName, i
|
||||
};
|
||||
|
||||
const loadImage = async () => {
|
||||
if (!image) {
|
||||
if (!image || image === "") {
|
||||
console.log("Loading ", image);
|
||||
await loadDefaultImage();
|
||||
}
|
||||
};
|
||||
loadImage().then(r => null);
|
||||
loadImage().then(() => null);
|
||||
}, [image]);
|
||||
|
||||
const pickImage = async () => {
|
||||
let result = await ImagePicker.launchImageLibraryAsync({ base64: true });
|
||||
if (!result.canceled && result.assets.length > 0) {
|
||||
if (result.assets[0].base64 !== image) { // Only update if the image actually changes
|
||||
setImage(result.assets[0].base64 || image);
|
||||
console.log("Picking Image");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
// Check if the name or image has changed
|
||||
const hasChanged = name !== initialName || image !== initialImage;
|
||||
if (hasChanged) {
|
||||
setChanged(true);
|
||||
}
|
||||
onClose(); // Close the profile screen
|
||||
};
|
||||
|
||||
return (
|
||||
<Portal>
|
||||
<Dialog visible={visible} onDismiss={onClose} style={{ backgroundColor: colors.background }}>
|
||||
<Dialog
|
||||
visible={visible}
|
||||
onDismiss={() => {
|
||||
if (!isNameEmpty) { // Prevent closing if name is empty
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
style={{ backgroundColor: colors.background }}>
|
||||
<Dialog.Title style={{ color: colors.onBackground, textAlign: 'center' }}>Edit Your Profile</Dialog.Title>
|
||||
<Dialog.Content>
|
||||
<Avatar.Image
|
||||
@ -81,7 +114,12 @@ const ProfileScreen: React.FC<ProfileScreenProps> = ({ visible, name, setName, i
|
||||
label="Your Pet's Name"
|
||||
mode="outlined"
|
||||
value={name}
|
||||
onChangeText={setName}
|
||||
onChangeText={(newName) => {
|
||||
if (newName !== name) { // Only trigger change if it's different
|
||||
setName(newName);
|
||||
console.log("Name change");
|
||||
}
|
||||
}}
|
||||
style={{ marginBottom: 15, backgroundColor: colors.surface }}
|
||||
placeholderTextColor={colors.onSurface}
|
||||
theme={{ colors: { text: colors.onSurface } }}
|
||||
@ -89,38 +127,20 @@ const ProfileScreen: React.FC<ProfileScreenProps> = ({ visible, name, setName, i
|
||||
</Dialog.Content>
|
||||
<Dialog.Actions>
|
||||
<Button
|
||||
onPress={onClose}
|
||||
onPress={handleSave}
|
||||
mode="contained"
|
||||
style={{ backgroundColor: colors.secondary }}
|
||||
labelStyle={{ color: colors.onPrimary }}>Save</Button>
|
||||
disabled={isNameEmpty} // Disable if name is empty
|
||||
style={{
|
||||
backgroundColor: isNameEmpty ? colors.tertiary : colors.secondary, // Dim the button
|
||||
opacity: isNameEmpty ? 0.5 : 1, // Visually dim the button
|
||||
}}
|
||||
labelStyle={{ color: colors.onPrimary }}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Dialog.Actions>
|
||||
</Dialog>
|
||||
</Portal>
|
||||
/*
|
||||
<Portal>
|
||||
<Dialog visible={visible} onDismiss={onClose} style={{ backgroundColor: colors.background }}>
|
||||
<Dialog.Title>Edit Your Profile</Dialog.Title>
|
||||
<Dialog.Content>
|
||||
<Avatar.Image
|
||||
size={100}
|
||||
source={image ? { uri: `data:image/png;base64,${image}` } : require("../assets/images/default_profile_image.png")}
|
||||
style={{ alignSelf: 'center', marginBottom: 15 }}
|
||||
/>
|
||||
<Button onPress={pickImage} mode="contained" style={{ backgroundColor: colors.primary, marginBottom: 10 }}>Change Profile Picture</Button>
|
||||
<TextInput
|
||||
label="Your Pet's Name"
|
||||
mode="outlined"
|
||||
value={name}
|
||||
onChangeText={setName}
|
||||
style={{ marginBottom: 15, backgroundColor: colors.surface, color: colors.primary }}
|
||||
/>
|
||||
</Dialog.Content>
|
||||
<Dialog.Actions>
|
||||
<Button onPress={onClose} mode="contained" style={{ backgroundColor: colors.secondary }}>Save</Button>
|
||||
</Dialog.Actions>
|
||||
</Dialog>
|
||||
</Portal>
|
||||
*/
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -188,7 +188,7 @@ const StatusPage: React.FC<StatusProps> = ({ id, name, image, currentStatus, set
|
||||
<View style={styles.listRow}>
|
||||
<View style={styles.listColumn}>
|
||||
<List.Section>
|
||||
<List.Subheader style={[styles.listSubheader, { color: theme.colors.onSurface }]}>On the Way</List.Subheader>
|
||||
<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 => (
|
||||
@ -207,7 +207,7 @@ const StatusPage: React.FC<StatusProps> = ({ id, name, image, currentStatus, set
|
||||
</View>
|
||||
<View style={styles.listColumn}>
|
||||
<List.Section>
|
||||
<List.Subheader style={[styles.listSubheader, { color: theme.colors.onSurface }]}>Arrived</List.Subheader>
|
||||
<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 => (
|
||||
@ -234,9 +234,9 @@ const StatusPage: React.FC<StatusProps> = ({ id, name, image, currentStatus, set
|
||||
onPress={() => handleStatusPress("On the Way")}
|
||||
style={[
|
||||
styles.actionButton,
|
||||
{ backgroundColor: currentStatus === "On the Way" ? pulseColorOnTheWay : theme.colors.primary }
|
||||
{ backgroundColor: currentStatus === "On the Way" ? pulseColorOnTheWay : theme.colors.inversePrimary }
|
||||
]}
|
||||
labelStyle={{ color: theme.colors.onPrimary }}>
|
||||
labelStyle={{ color: currentStatus === "On the Way" ? theme.colors.inversePrimary : theme.colors.primary }}>
|
||||
{getButtonLabel("On the Way")}
|
||||
</Button>
|
||||
</Animated.View>
|
||||
@ -246,9 +246,9 @@ const StatusPage: React.FC<StatusProps> = ({ id, name, image, currentStatus, set
|
||||
onPress={() => handleStatusPress("Arrived")}
|
||||
style={[
|
||||
styles.actionButton,
|
||||
{ backgroundColor: currentStatus === "Arrived" ? pulseColorArrived : theme.colors.primary }
|
||||
{ backgroundColor: currentStatus === "Arrived" ? pulseColorArrived : theme.colors.inversePrimary }
|
||||
]}
|
||||
labelStyle={{ color: theme.colors.onPrimary }}>
|
||||
labelStyle={{ color: currentStatus === "Arrived" ? theme.colors.inversePrimary : theme.colors.primary }}>
|
||||
{getButtonLabel("Arrived")}
|
||||
</Button>
|
||||
</Animated.View>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {View, StyleSheet } from "react-native";
|
||||
import {View, StyleSheet, Text } from "react-native";
|
||||
import { useTheme } from "react-native-paper";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
@ -10,10 +10,12 @@ import Nav from "@/app/Nav";
|
||||
const Index = () => {
|
||||
const { colors } = useTheme();
|
||||
const [isProfileActive, setProfileActive] = useState(false);
|
||||
const [userId, setUserId] = useState(uuidv4());
|
||||
const [userId, setUserId] = useState("");
|
||||
const [userName, setUserName] = useState("");
|
||||
const [userImage, setUserImage] = useState("");
|
||||
const [userStatus, setUserStatus] = useState("");
|
||||
const [userDataChanged, setUserDataChanged] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true); // New loading state
|
||||
|
||||
useEffect(() => {
|
||||
const loadUserData = async () => {
|
||||
@ -21,40 +23,56 @@ const Index = () => {
|
||||
const storedUserId = await AsyncStorage.getItem("userId");
|
||||
const storedUserName = await AsyncStorage.getItem("userName");
|
||||
const storedUserImage = await AsyncStorage.getItem("userImage");
|
||||
if(storedUserId) {
|
||||
|
||||
if (storedUserId) {
|
||||
setUserId(storedUserId || uuidv4());
|
||||
setUserName(storedUserName || "");
|
||||
setUserImage(storedUserImage || "");
|
||||
setProfileActive(false);
|
||||
}else{
|
||||
} else {
|
||||
setUserId(uuidv4());
|
||||
setUserName("");
|
||||
setUserImage("");
|
||||
setProfileActive(true);
|
||||
}
|
||||
|
||||
console.log("Loading data ", userId);
|
||||
} catch (error) {
|
||||
console.error("Error loading user data:", error);
|
||||
} finally {
|
||||
setIsLoading(false); // Mark loading as complete
|
||||
}
|
||||
};
|
||||
loadUserData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!userDataChanged) return;
|
||||
|
||||
const saveUserData = async () => {
|
||||
try {
|
||||
console.log("Saving data ", userId);
|
||||
await AsyncStorage.setItem("userId", userId);
|
||||
await AsyncStorage.setItem("userName", userName);
|
||||
await AsyncStorage.setItem("userImage", userImage);
|
||||
setUserDataChanged(false);
|
||||
} catch (error) {
|
||||
console.error("Error saving user data:", error);
|
||||
}
|
||||
};
|
||||
saveUserData();
|
||||
}, [userId, userName, userImage]);
|
||||
}, [userDataChanged]);
|
||||
|
||||
if (isLoading) {
|
||||
console.log("Still loading");
|
||||
return (
|
||||
<View style={[styles.container, { backgroundColor: colors.background, justifyContent: 'center', alignItems: 'center' }]}>
|
||||
<Text>Loading...</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[styles.container,{ backgroundColor: colors.background }]}>
|
||||
<View style={[styles.container, { backgroundColor: colors.background }]}>
|
||||
<Nav
|
||||
toggleProfile={() => setProfileActive(true)}
|
||||
/>
|
||||
@ -73,6 +91,7 @@ const Index = () => {
|
||||
setName={setUserName}
|
||||
image={userImage}
|
||||
setImage={setUserImage}
|
||||
setChanged={setUserDataChanged}
|
||||
onClose={() => setProfileActive(false)}
|
||||
/>
|
||||
</View>
|
||||
|
Loading…
Reference in New Issue
Block a user