tech-tracker-expo/components/auth/Profile_Avatar.tsx

151 lines
4.2 KiB
TypeScript

import { useState, useEffect } from 'react'
import { supabase } from '@/lib/supabase'
import { StyleSheet, Alert, Image, TouchableOpacity } from 'react-native'
import * as ImagePicker from 'expo-image-picker'
//import { ImageManipulator } from 'expo-image-manipulator';
import { ThemedView, ThemedText, ThemedTextButton } from '../theme';
import { IconSymbol } from '@/components/ui/IconSymbol';
interface Props {
size: number
url: string | null
onUpload: (filePath: string) => void
}
export default function Avatar({ url, size = 150, onUpload }: Props) {
const [uploading, setUploading] = useState(false)
const [avatarUrl, setAvatarUrl] = useState<string | null>(null)
const avatarSize = { height: size, width: size }
useEffect(() => {
if (url) downloadImage(url)
}, [url])
async function downloadImage(path: string) {
try {
const { data, error } = await supabase.storage.from('avatars').download(path)
if (error) {
throw error
}
const fr = new FileReader()
fr.readAsDataURL(data)
fr.onload = () => {
setAvatarUrl(fr.result as string)
}
} catch (error) {
if (error instanceof Error) {
console.log('Error downloading image: ', error.message)
}
}
};
async function uploadAvatar() {
try {
setUploading(true)
const { data: { user } } = await supabase.auth.getUser();
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images, // Restrict to only images
allowsMultipleSelection: false, // Can only select one image
allowsEditing: true, // Allows the user to crop / rotate their photo before uploading it
quality: 1,
exif: false, // We don't want nor need that data.
});
if (result.canceled || !result.assets || result.assets.length === 0) {
console.log('User cancelled image picker.');
return;
}
//const manipulatedImage = await ImageManipulator.manipulate(result.assets[0].uri)
//.resize({ width: 300 })
//.renderAsync();
//const manipulateResult = await manipulateAsync(
//result.assets[0].uri,
//[{resize: {width: 300, height: 300}}],
//{compress: 0.7, format: SaveFormat.JPEG}
//);
const image = result.assets[0];
console.log('Got image', image)
if (!image.uri) {
throw new Error('No image uri!') // Realistically, this should never happen, but just in case...
}
const arraybuffer = await fetch(image.uri).then((res) => res.arrayBuffer())
const fileExt = image.uri?.split('.').pop()?.toLowerCase() ?? 'jpeg'
const path = `${Date.now()}.${fileExt}`
const { data, error: uploadError } = await supabase.storage
.from('avatars')
.upload(path, arraybuffer, {
contentType: image.mimeType ?? 'image/jpeg',
});
const { data: updateData, error: updateError } = await supabase
.from('profiles')
.update({ avatar_url: data?.path })
.eq('id', user?.id);
if (uploadError) {
throw uploadError
}
onUpload(data.path)
} catch (error) {
if (error instanceof Error) {
Alert.alert(error.message)
} else {
throw error
}
} finally {
setUploading(false)
}
};
return (
<TouchableOpacity
onPress={uploadAvatar}
style={styles.avatarContainer}
>
{avatarUrl ? (
<Image source={{ uri: avatarUrl }} style={styles.avatar} />
) : (
<ThemedView style={styles.avatarPlaceholder}>
<IconSymbol name="person.fill" size={50} color="#999" />
</ThemedView>
)}
<ThemedText style={styles.changePhotoText}>Change Photo</ThemedText>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
avatarContainer: {
alignItems: 'center',
marginTop: 20,
marginBottom: 30,
},
avatar: {
width: 120,
height: 120,
borderRadius: 60,
},
avatarPlaceholder: {
width: 120,
height: 120,
borderRadius: 60,
backgroundColor: '#E1E1E1',
justifyContent: 'center',
alignItems: 'center',
},
changePhotoText: {
marginTop: 8,
color: '#007AFF',
fontSize: 16,
},
});