// https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/src/view/com/util/UserAvatar.tsx
import React, {memo, useMemo} from 'react'
import {Image, Pressable, StyleSheet, View} from 'react-native'
import {Image as RNImage} from 'react-native-image-crop-picker'
import Svg, {Circle, Path, Rect} from 'react-native-svg'
import {AppBskyActorDefs, ModerationUI} from '@atproto/api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useQueryClient} from '@tanstack/react-query'
import {usePalette} from '#/lib/hooks/usePalette'
import {
useCameraPermission,
usePhotoLibraryPermission,
} from '#/lib/hooks/usePermissions'
import {makeProfileLink} from '#/lib/routes/links'
import {colors} from '#/lib/styles'
import {logger} from '#/logger'
import {isAndroid, isNative, isWeb} from '#/platform/detection'
import {precacheProfile} from '#/state/queries/profile'
import {HighPriorityImage} from '#/view/com/util/images/Image'
import {tokens, useTheme} from '#/alf'
import {useSheetWrapper} from '#/components/Dialog/sheet-wrapper'
import {
Camera_Filled_Stroke2_Corner0_Rounded as CameraFilled,
Camera_Stroke2_Corner0_Rounded as Camera,
} from '#/components/icons/Camera'
import {StreamingLive_Stroke2_Corner0_Rounded as Library} from '#/components/icons/StreamingLive'
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
import {Link} from '#/components/Link'
import {MediaInsetBorder} from '#/components/MediaInsetBorder'
import * as Menu from '#/components/Menu'
import {ProfileHoverCard} from '#/components/ProfileHoverCard'
import {openCamera, openCropper, openPicker} from '../../../lib/media/picker'
export type UserAvatarType = 'user' | 'algo' | 'list' | 'labeler'
interface BaseUserAvatarProps {
type?: UserAvatarType
shape?: 'circle' | 'square'
size: number
avatar?: string | null
}
interface UserAvatarProps extends BaseUserAvatarProps {
moderation?: ModerationUI
usePlainRNImage?: boolean
onLoad?: () => void
}
interface EditableUserAvatarProps extends BaseUserAvatarProps {
onSelectNewAvatar: (img: RNImage | null) => void
}
interface PreviewableUserAvatarProps extends BaseUserAvatarProps {
moderation?: ModerationUI
profile: AppBskyActorDefs.ProfileViewBasic
disableHoverCard?: boolean
onBeforePress?: () => void
}
const BLUR_AMOUNT = isWeb ? 5 : 100
let DefaultAvatar = ({
type,
shape: overrideShape,
size,
}: {
type: UserAvatarType
shape?: 'square' | 'circle'
size: number
}): React.ReactNode => {
const finalShape = overrideShape ?? (type === 'user' ? 'circle' : 'square')
if (type === 'algo') {
// TODO: shape=circle
// Font Awesome Pro 6.4.0 by @fontawesome -https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc.
return (
)
}
if (type === 'list') {
// TODO: shape=circle
// Font Awesome Pro 6.4.0 by @fontawesome -https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc.
return (
)
}
if (type === 'labeler') {
return (
)
}
// TODO: shape=square
return (
)
}
DefaultAvatar = memo(DefaultAvatar)
export {DefaultAvatar}
let UserAvatar = ({
type = 'user',
shape: overrideShape,
size,
avatar,
moderation,
usePlainRNImage = false,
onLoad,
}: UserAvatarProps): React.ReactNode => {
const pal = usePalette('default')
const backgroundColor = pal.colors.backgroundLight
const finalShape = overrideShape ?? (type === 'user' ? 'circle' : 'square')
const aviStyle = useMemo(() => {
if (finalShape === 'square') {
return {
width: size,
height: size,
borderRadius: size > 32 ? 8 : 3,
backgroundColor,
}
}
return {
width: size,
height: size,
borderRadius: Math.floor(size / 2),
backgroundColor,
}
}, [finalShape, size, backgroundColor])
const alert = useMemo(() => {
if (!moderation?.alert) {
return null
}
return (