All files / src/components UserAvatar.tsx

83.78% Statements 31/37
71.42% Branches 10/14
75% Functions 3/4
83.78% Lines 31/37

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 561x 1x                         46x 46x 46x 46x   46x 46x 46x   1x 46x 46x 46x 46x 46x 46x 46x 46x 46x 40x 46x 46x 46x 46x 46x   46x 46x 46x               46x   46x   46x  
import { useState, useEffect } from 'react';
import './UserAvatar.css';
 
export type UserAvatarVariant = 'accent' | 'muted';
 
export interface UserAvatarProps {
  username: string;
  displayName?: string | null;
  avatarUrl?: string | null;
  className?: string;
  size?: 'sm' | 'md';
  variant?: UserAvatarVariant;
}
 
function initialLetter(username: string, displayName?: string | null): string {
  const from = displayName?.trim() || username;
  return from.charAt(0).toUpperCase() || '?';
}
 
function hasUsableSrc(src: string | null | undefined): boolean {
  return typeof src === 'string' && src.trim().length > 0;
}
 
export function UserAvatar({
  username,
  displayName,
  avatarUrl,
  className = '',
  size = 'sm',
  variant = 'muted',
}: UserAvatarProps) {
  const [failed, setFailed] = useState(false);
  useEffect(() => {
    setFailed(false);
  }, [avatarUrl]);
  const showImg = hasUsableSrc(avatarUrl) && !failed;
  const letter = initialLetter(username, displayName);
  const sizeClass = size === 'md' ? 'user-avatar--md' : 'user-avatar--sm';
  const variantClass = variant === 'accent' ? 'user-avatar--accent' : 'user-avatar--muted';
 
  return (
    <div className={`user-avatar ${sizeClass} ${variantClass} ${className}`.trim()}>
      {showImg ? (
        <img
          src={avatarUrl!.trim()}
          alt=""
          className="user-avatar-img"
          onError={() => setFailed(true)}
        />
      ) : (
        <span className="user-avatar-fallback">{letter}</span>
      )}
    </div>
  );
}