91 lines
		
	
	
	
		
			2.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			91 lines
		
	
	
	
		
			2.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { useState, useCallback } from 'react';
 | |
| 
 | |
| import classNames from 'classnames';
 | |
| import { Link } from 'react-router-dom';
 | |
| 
 | |
| import { useHovering } from 'mastodon/hooks/useHovering';
 | |
| import { autoPlayGif } from 'mastodon/initial_state';
 | |
| import type { Account } from 'mastodon/models/account';
 | |
| 
 | |
| interface Props {
 | |
|   account:
 | |
|     | Pick<Account, 'id' | 'acct' | 'avatar' | 'avatar_static'>
 | |
|     | undefined; // FIXME: remove `undefined` once we know for sure its always there
 | |
|   size?: number;
 | |
|   style?: React.CSSProperties;
 | |
|   inline?: boolean;
 | |
|   animate?: boolean;
 | |
|   withLink?: boolean;
 | |
|   counter?: number | string;
 | |
|   counterBorderColor?: string;
 | |
| }
 | |
| 
 | |
| export const Avatar: React.FC<Props> = ({
 | |
|   account,
 | |
|   animate = autoPlayGif,
 | |
|   size = 20,
 | |
|   inline = false,
 | |
|   withLink = false,
 | |
|   style: styleFromParent,
 | |
|   counter,
 | |
|   counterBorderColor,
 | |
| }) => {
 | |
|   const { hovering, handleMouseEnter, handleMouseLeave } = useHovering(animate);
 | |
|   const [loading, setLoading] = useState(true);
 | |
|   const [error, setError] = useState(false);
 | |
| 
 | |
|   const style = {
 | |
|     ...styleFromParent,
 | |
|     width: `${size}px`,
 | |
|     height: `${size}px`,
 | |
|   };
 | |
| 
 | |
|   const src = hovering || animate ? account?.avatar : account?.avatar_static;
 | |
| 
 | |
|   const handleLoad = useCallback(() => {
 | |
|     setLoading(false);
 | |
|   }, [setLoading]);
 | |
| 
 | |
|   const handleError = useCallback(() => {
 | |
|     setError(true);
 | |
|   }, [setError]);
 | |
| 
 | |
|   const avatar = (
 | |
|     <div
 | |
|       className={classNames('account__avatar', {
 | |
|         'account__avatar--inline': inline,
 | |
|         'account__avatar--loading': loading,
 | |
|       })}
 | |
|       onMouseEnter={handleMouseEnter}
 | |
|       onMouseLeave={handleMouseLeave}
 | |
|       style={style}
 | |
|     >
 | |
|       {src && !error && (
 | |
|         <img src={src} alt='' onLoad={handleLoad} onError={handleError} />
 | |
|       )}
 | |
| 
 | |
|       {counter && (
 | |
|         <div
 | |
|           className='account__avatar__counter'
 | |
|           style={{ borderColor: counterBorderColor }}
 | |
|         >
 | |
|           {counter}
 | |
|         </div>
 | |
|       )}
 | |
|     </div>
 | |
|   );
 | |
| 
 | |
|   if (withLink) {
 | |
|     return (
 | |
|       <Link
 | |
|         to={`/@${account?.acct}`}
 | |
|         title={`@${account?.acct}`}
 | |
|         data-hover-card-account={account?.id}
 | |
|       >
 | |
|         {avatar}
 | |
|       </Link>
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   return avatar;
 | |
| };
 |