72 lines
1.8 KiB
TypeScript
72 lines
1.8 KiB
TypeScript
|
|
import { StyleProp, TextStyle, View, ViewStyle } from "react-native"
|
||
|
|
import { Image, ImageStyle } from "expo-image"
|
||
|
|
|
||
|
|
import { useAppTheme } from "@/theme/context"
|
||
|
|
import type { ThemedStyle } from "@/theme/types"
|
||
|
|
import { s } from "@/utils/responsive"
|
||
|
|
|
||
|
|
import { Text } from "./Text"
|
||
|
|
|
||
|
|
export interface AvatarProps {
|
||
|
|
/**
|
||
|
|
* The image URI to display
|
||
|
|
*/
|
||
|
|
uri?: string | null
|
||
|
|
/**
|
||
|
|
* Fallback text to display when no image (usually first letter of name)
|
||
|
|
*/
|
||
|
|
fallback?: string
|
||
|
|
/**
|
||
|
|
* Size of the avatar (width and height)
|
||
|
|
* @default 80
|
||
|
|
*/
|
||
|
|
size?: number
|
||
|
|
/**
|
||
|
|
* Optional style override for the container
|
||
|
|
*/
|
||
|
|
style?: StyleProp<ViewStyle>
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* A reusable Avatar component with expo-image for efficient caching.
|
||
|
|
* Displays an image or a fallback letter when no image is available.
|
||
|
|
*/
|
||
|
|
export function Avatar(props: AvatarProps) {
|
||
|
|
const { uri, fallback = "U", size = s(80), style } = props
|
||
|
|
const { themed, theme } = useAppTheme()
|
||
|
|
|
||
|
|
const borderRadius = size / 2
|
||
|
|
const fontSize = size * 0.4
|
||
|
|
|
||
|
|
if (uri) {
|
||
|
|
return (
|
||
|
|
<Image
|
||
|
|
source={{ uri }}
|
||
|
|
style={[$image, { width: size, height: size, borderRadius }, style as ImageStyle]}
|
||
|
|
contentFit="cover"
|
||
|
|
cachePolicy="memory-disk"
|
||
|
|
transition={200}
|
||
|
|
placeholder={{ blurhash: "L6PZfSi_.AyE_3t7t7R**0o#DgR4" }}
|
||
|
|
/>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<View style={[themed($placeholder), { width: size, height: size, borderRadius }, style]}>
|
||
|
|
<Text style={{ color: theme.colors.palette.neutral100, fontSize, fontWeight: "bold" }}>
|
||
|
|
{fallback.charAt(0).toUpperCase()}
|
||
|
|
</Text>
|
||
|
|
</View>
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
const $image: ImageStyle = {
|
||
|
|
backgroundColor: "#e1e1e1",
|
||
|
|
}
|
||
|
|
|
||
|
|
const $placeholder: ThemedStyle<ViewStyle> = ({ colors }) => ({
|
||
|
|
backgroundColor: colors.tint,
|
||
|
|
justifyContent: "center",
|
||
|
|
alignItems: "center",
|
||
|
|
})
|