pogdark-app/components/Profile.tsx

171 lines
7.4 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { Platform, View, TouchableOpacity } from "react-native";
import { Button, TextInput, Dialog, Portal, Avatar, useTheme, Text } from "react-native-paper";
import { Asset } from 'expo-asset';
import * as FileSystem from 'expo-file-system';
import * as ImagePicker from "expo-image-picker";
import themes from '@/assets/themes';
import styles from "@/assets/styles";
import log from "@/util/log"
import featureFlags from '@/util/featureFlags';
interface ProfileScreenProps {
visible: boolean;
id: string;
name: string;
setName: (name: string) => void;
image: string;
setImage: (image: string) => void;
setChanged: (dataChanged: boolean) => void;
setTheme: (theme: string) => void;
currentTheme: string;
onClose: () => void;
}
const Profile: React.FC<ProfileScreenProps> = ({ visible, name, setName, image, setImage, setChanged, currentTheme, setTheme, onClose }) => {
const theme = useTheme();
const isNameEmpty = !name.trim();
const themeColors = ['red', 'blue', 'yellow', 'green', 'orange', 'purple'];
// Track the initial values when the component first mounts
const [initialName, setInitialName] = useState(name);
const [initialImage, setInitialImage] = useState(image);
const [initialTheme, setInitialTheme] = useState(currentTheme);
useEffect(() => {
if (visible) {
setInitialName(name); // Store initial name when the profile opens
setInitialImage(image);
setInitialTheme(currentTheme)// Store initial image when the profile opens
}
}, [visible]); // Reset when the dialog is opened
useEffect(() => {
const loadDefaultImage = async () => {
const asset = Asset.fromModule(require("../assets/images/default_profile_image.png"));
await asset.downloadAsync();
if (Platform.OS === 'web') {
const response = await fetch(asset.uri);
const blob = await response.blob();
return new Promise((resolve) => {
const reader = new FileReader();
reader.onloadend = () => {
const base64String = reader.result?.toString().replace(/^data:.+;base64,/, '');
resolve(base64String);
if (typeof base64String == "string") {
setImage(base64String);
} else {
throw new Error("Failed to load asset.");
}
};
reader.readAsDataURL(blob);
});
} else if (asset.uri) {
const base64 = await FileSystem.readAsStringAsync(asset.uri, { encoding: FileSystem.EncodingType.Base64 });
setImage(base64);
}
};
const loadImage = async () => {
if (!image || image === "") {
log.debug("Loading ", image);
await loadDefaultImage();
}
};
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);
log.debug("Picking Image");
}
}
};
const handleSave = () => {
// Check if the name or image has changed
const hasChanged = name !== initialName || image !== initialImage || currentTheme !== initialTheme;
if (hasChanged) {
setChanged(true);
}
onClose(); // Close the profile screen
};
return (
<Portal>
<Dialog
visible={visible}
onDismiss={() => {
if (!isNameEmpty) { // Prevent closing if name is empty
onClose();
}
}}
style={{ backgroundColor: theme.colors.background }}>
<Dialog.Title style={{ color: theme.colors.onBackground, textAlign: 'center', fontFamily: "Light"}}>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: theme.colors.primaryContainer, marginBottom: 10 }}
labelStyle={{ color: theme.colors.primary, fontFamily: "SpaceReg" }}
>
Change Profile Picture
</Button>
<TextInput
label="Your Pet's Name"
mode="outlined"
value={name}
onChangeText={(newName) => {
if (newName !== name) { // Only trigger change if it's different
setName(newName);
log.debug("Name change");
}
}}
style={{ marginBottom: 15, fontFamily: "SpaceReg" }}
placeholderTextColor={theme.colors.primary}
textColor={theme.colors.primary}
theme={{ colors: { text: theme.colors.primary }}}
/>
{featureFlags.enableThemeSelection && (
<>
<Text style={{ color: theme.colors.primary, fontSize: 18, fontFamily: "SpaceReg", textAlign: 'center' }}>Choose Theme</Text>
<View style={styles.themeContainer}>
{themeColors.map((userTheme) => (
<TouchableOpacity
key={userTheme}
style={[styles.themeButton, { backgroundColor: themes[userTheme as keyof typeof themes]['light'].colors.primary }]}
onPress={() => {setTheme(userTheme); log.debug("Changing Theme: ", userTheme)}}
>
<View style={[styles.halfCircle, { backgroundColor: themes[userTheme as keyof typeof themes]['dark'].colors.primary }]} />
</TouchableOpacity>
))}
</View>
</>
)}
</Dialog.Content>
<Dialog.Actions>
<Button
onPress={handleSave}
mode="contained"
disabled={isNameEmpty} // Disable if name is empty
style={{
backgroundColor: theme.colors.primaryContainer,
opacity: isNameEmpty ? 0.5 : 1, // Visually dim the button
}}
labelStyle={{ color: theme.colors.primary, fontFamily: "SpaceReg" }}>Save</Button>
</Dialog.Actions>
</Dialog>
</Portal>
);
};
export default Profile;