/ src / components / Avatar.tsx
Avatar.tsx
 1  import {
 2    Avatar as MaterialAvatar,
 3    AvatarProps as MaterialAvatarProps,
 4    Badge,
 5    Skeleton,
 6  } from '@mui/material';
 7  import { blo } from 'blo';
 8  import { ReactNode, useEffect, useState } from 'react';
 9  
10  export enum AvatarSize {
11    XS = 20,
12    SM = 22,
13    MD = 24,
14    LG = 32,
15    XL = 40,
16  }
17  
18  export interface AvatarProps extends Omit<MaterialAvatarProps, 'src'> {
19    image?: string;
20    fallbackImage?: string;
21    size?: AvatarSize | number;
22    loading?: boolean;
23    badge?: ReactNode;
24    invisibleBadge?: boolean;
25  }
26  
27  export const Avatar: React.FC<AvatarProps> = ({
28    image,
29    fallbackImage = blo('0x'),
30    size = AvatarSize.MD,
31    sx,
32    loading = false,
33    invisibleBadge = false,
34    badge,
35    ...rest
36  }) => {
37    const [useFallbackImage, setUseFallbackImage] = useState(false);
38  
39    useEffect(() => {
40      setUseFallbackImage(false);
41    }, [image]);
42  
43    return loading ? (
44      <Skeleton variant="circular" width={size} height={size} />
45    ) : (
46      <Badge
47        overlap="circular"
48        anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
49        badgeContent={badge}
50        invisible={invisibleBadge || loading}
51      >
52        <MaterialAvatar
53          src={useFallbackImage || !image ? fallbackImage : image}
54          sx={{ width: size, height: size, border: '1px solid #FAFBFC1F', ...sx }}
55          alt="avatar"
56          imgProps={{
57            onError: () => setUseFallbackImage(true),
58          }}
59          {...rest}
60        />
61      </Badge>
62    );
63  };