diff --git a/src/components/image.js b/src/components/image.js index 157c8c2..66d37b7 100644 --- a/src/components/image.js +++ b/src/components/image.js @@ -1,12 +1,11 @@ import React from 'react'; import PropTypes from 'prop-types'; +import md5 from 'md5'; import { parseSize, calculateBorderRadius, getNullableText } from '../utils'; import Wrapper from './wrapper'; -export default -class AvatarImage extends React.PureComponent { - +export default class AvatarImage extends React.PureComponent { static propTypes = { alt: PropTypes.oneOfType([ PropTypes.string, @@ -19,7 +18,7 @@ class AvatarImage extends React.PureComponent { name: PropTypes.string, value: PropTypes.string, avatar: PropTypes.object, - + email: PropTypes.string, // Add email prop for Gravatar className: PropTypes.string, unstyled: PropTypes.bool, round: PropTypes.oneOfType([ @@ -31,14 +30,35 @@ class AvatarImage extends React.PureComponent { PropTypes.number, PropTypes.string ]), - } + fallback: PropTypes.string, // Optional fallback prop for Gravatar fallback + onError: PropTypes.func // Allow passing an external onError prop + }; static defaultProps = { className: '', round: false, size: 100, - unstyled: false - } + unstyled: false, + fallback: 'blank', // Default to 'blank' if no fallback is provided + onError: null // No default external onError + }; + + handleImageError = (e) => { + const { email, fallback, onError } = this.props; + + // Only run fallback logic if fallback is explicitly provided + if (fallback) { + const emailHash = email ? md5(email.trim().toLowerCase()) : ''; // Hash the email for Gravatar + + // Set the fallback URL with the hashed email and fallback option + e.target.src = `https://www.gravatar.com/avatar/${emailHash}?d=${encodeURIComponent(fallback)}`; + } + + // If there's an external onError handler, call it as well + if (onError) { + onError(e); + } + }; render() { const { @@ -49,7 +69,8 @@ class AvatarImage extends React.PureComponent { title, name, value, - avatar + avatar, + email, // Email used for Gravatar hash } = this.props; const size = parseSize(this.props.size); @@ -70,7 +91,8 @@ class AvatarImage extends React.PureComponent { src={avatar.src} alt={getNullableText(alt, name || value)} title={getNullableText(title, name || value)} - onError={avatar.onRenderFailed} /> + onError={this.handleImageError} // Call the error handler to use fallback and external onError + /> ); } diff --git a/src/sources/Gravatar.js b/src/sources/Gravatar.js index 473c3f5..fa24962 100644 --- a/src/sources/Gravatar.js +++ b/src/sources/Gravatar.js @@ -5,13 +5,25 @@ import md5 from 'md5'; import { getImageSize } from '../utils'; +// Define valid Gravatar fallback options +const validGravatarFallbacks = [ + '404', // Do not load any image, return a 404 + 'mp', // Mystery Person + 'identicon', // Geometric pattern based on email hash + 'monsterid', // A generated monster image + 'wavatar', // A generated face + 'retro', // 8-bit retro avatar + 'robohash', // Robot based on hash + 'blank' // Transparent, blank image +]; export default class GravatarSource { static propTypes = { email: PropTypes.string, - md5Email: PropTypes.string + md5Email: PropTypes.string, + fallback: PropTypes.string } props = null; @@ -29,7 +41,13 @@ class GravatarSource { const email = props.md5Email || md5(props.email); const size = getImageSize(props.size); - let url = `https://secure.gravatar.com/avatar/${email}?d=404`; + // Check if the provided fallback is a valid Gravatar option or a URL + let fallback = validGravatarFallbacks.includes(props.fallback) + ? props.fallback + : encodeURIComponent(props.fallback || 'blank'); // Use fallback, or default to 'blank' + + // Construct the Gravatar URL with the valid fallback + let url = `https://secure.gravatar.com/avatar/${email}?d=${fallback}`; if (size) url += `&s=${size}`;