Improve fallback of Group Avatars not loading

environments/review-improve-gr-9h1bjf/deployments/3257
Chewbacca 1 year ago
parent e5cf1dfa85
commit 36be68cdcc

@ -1,6 +1,6 @@
import React from 'react';
import { defineMessages, useIntl } from 'react-intl';
import GroupHeaderImage from 'soapbox/features/group/components/group-header-image';
import GroupMemberCount from 'soapbox/features/group/components/group-member-count';
import GroupPrivacy from 'soapbox/features/group/components/group-privacy';
import GroupRelationship from 'soapbox/features/group/components/group-relationship';
@ -10,17 +10,11 @@ import { HStack, Stack, Text } from './ui';
import type { Group as GroupEntity } from 'soapbox/types/entities';
const messages = defineMessages({
groupHeader: { id: 'group.header.alt', defaultMessage: 'Group header' },
});
interface IGroupCard {
group: GroupEntity
}
const GroupCard: React.FC<IGroupCard> = ({ group }) => {
const intl = useIntl();
return (
<Stack
className='relative h-[240px] rounded-lg border border-solid border-gray-300 bg-white dark:border-primary-800 dark:bg-primary-900'
@ -28,12 +22,10 @@ const GroupCard: React.FC<IGroupCard> = ({ group }) => {
>
{/* Group Cover Image */}
<Stack grow className='relative basis-1/2 rounded-t-lg bg-primary-100 dark:bg-gray-800'>
{group.header && (
<img
<GroupHeaderImage
group={group}
className='absolute inset-0 h-full w-full rounded-t-lg object-cover'
src={group.header} alt={intl.formatMessage(messages.groupHeader)}
/>
)}
</Stack>
{/* Group Avatar */}

@ -3,7 +3,7 @@ import React, { useRef } from 'react';
import { useSettings } from 'soapbox/hooks';
interface IStillImage {
export interface IStillImage {
/** Image alt text. */
alt?: string
/** Extra class names for the outer <div> container. */

@ -1,34 +1,54 @@
import clsx from 'clsx';
import React from 'react';
import React, { useState } from 'react';
import StillImage from 'soapbox/components/still-image';
import StillImage, { IStillImage } from 'soapbox/components/still-image';
import Icon from '../icon/icon';
const AVATAR_SIZE = 42;
interface IAvatar {
/** URL to the avatar image. */
src: string
interface IAvatar extends Pick<IStillImage, 'src' | 'onError' | 'className'> {
/** Width and height of the avatar in pixels. */
size?: number
/** Extra class names for the div surrounding the avatar image. */
className?: string
}
/** Round profile avatar for accounts. */
const Avatar = (props: IAvatar) => {
const { src, size = AVATAR_SIZE, className } = props;
const [isAvatarMissing, setIsAvatarMissing] = useState<boolean>(false);
const handleLoadFailure = () => setIsAvatarMissing(true);
const style: React.CSSProperties = React.useMemo(() => ({
width: size,
height: size,
}), [size]);
if (isAvatarMissing) {
return (
<div
style={{
width: size,
height: size,
}}
className={clsx('flex items-center justify-center rounded-full bg-gray-200 dark:bg-gray-900', className)}
>
<Icon
src={require('@tabler/icons/photo-off.svg')}
className='h-4 w-4 text-gray-500 dark:text-gray-700'
/>
</div>
);
}
return (
<StillImage
className={clsx('rounded-full', className)}
style={style}
src={src}
alt='Avatar'
onError={handleLoadFailure}
/>
);
};

@ -0,0 +1,50 @@
import clsx from 'clsx';
import React, { useState } from 'react';
import { defineMessages, useIntl } from 'react-intl';
import { Icon } from 'soapbox/components/ui';
import type { Group } from 'soapbox/schemas';
const messages = defineMessages({
header: { id: 'group.header.alt', defaultMessage: 'Group header' },
});
interface IGroupHeaderImage {
group?: Group | false | null
className?: string
}
const GroupHeaderImage: React.FC<IGroupHeaderImage> = ({ className, group }) => {
const intl = useIntl();
const [isHeaderMissing, setIsHeaderMissing] = useState<boolean>(false);
if (!group || !group.header) {
return null;
}
if (isHeaderMissing) {
return (
<div
className={clsx(className, 'flex items-center justify-center bg-gray-200 dark:bg-gray-800/30')}
>
<Icon
src={require('@tabler/icons/photo-off.svg')}
className='h-6 w-6 text-gray-500 dark:text-gray-700'
/>
</div>
);
}
return (
<img
className={className}
src={group.header}
alt={intl.formatMessage(messages.header)}
onError={() => setIsHeaderMissing(true)}
/>
);
};
export default GroupHeaderImage;

@ -4,6 +4,7 @@ import { Link } from 'react-router-dom';
import GroupAvatar from 'soapbox/components/groups/group-avatar';
import { HStack, Stack, Text } from 'soapbox/components/ui';
import GroupActionButton from 'soapbox/features/group/components/group-action-button';
import GroupHeaderImage from 'soapbox/features/group/components/group-header-image';
import GroupMemberCount from 'soapbox/features/group/components/group-member-count';
import GroupPrivacy from 'soapbox/features/group/components/group-privacy';
@ -31,13 +32,10 @@ const GroupGridItem = forwardRef((props: IGroup, ref: React.ForwardedRef<HTMLDiv
ref={ref}
style={{ minHeight: 180 }}
>
{group.header && (
<img
src={group.header}
alt='Group cover'
<GroupHeaderImage
group={group}
className='absolute inset-0 object-cover'
/>
)}
<div
className='absolute inset-x-0 bottom-0 flex justify-center rounded-b-lg bg-gradient-to-t from-gray-900 to-transparent pb-8 pt-12 transition-opacity duration-500'

Loading…
Cancel
Save