import { type ReactNode, memo, forwardRef, useCallback, useRef, } from 'react'; import type { Ref, IUser } from '@growi/core'; import { pagePathUtils } from '@growi/core/dist/utils'; import dynamic from 'next/dynamic'; import { useRouter } from 'next/router'; import type { UncontrolledTooltipProps } from 'reactstrap'; const UncontrolledTooltip = dynamic(() => import('reactstrap').then(mod => mod.UncontrolledTooltip), { ssr: false }); const DEFAULT_IMAGE = '/images/icons/user.svg'; type UserPictureRootProps = { user: Partial, className?: string, children?: ReactNode, } const UserPictureRootWithoutLink = forwardRef((props, ref) => { return {props.children}; }); const UserPictureRootWithLink = forwardRef((props, ref) => { const router = useRouter(); const { user } = props; const href = pagePathUtils.userHomepagePath(user); const clickHandler = useCallback(() => { router.push(href); }, [href, router]); // Using tag here instead of tag because UserPicture is used in SearchResultList which is essentially a anchor tag. // Nested anchor tags causes a warning. // https://stackoverflow.com/questions/13052598/creating-anchor-tag-inside-anchor-taga return {props.children}; }); // wrapper with Tooltip const withTooltip = (UserPictureSpanElm: React.ForwardRefExoticComponent>) => { return (props: UserPictureRootProps) => { const { user } = props; const userPictureRef = useRef(null); return ( <> {props.children} @{user.username}
{user.name}
); }; }; /** * type guard to determine whether the specified object is IUser */ const isUserObj = (obj: Partial | Ref): obj is Partial => { return typeof obj !== 'string' && 'username' in obj; }; type Props = { user?: Partial | Ref | null, size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl', noLink?: boolean, noTooltip?: boolean, }; export const UserPicture = memo((props: Props): JSX.Element => { const { user, size, noLink, noTooltip, } = props; const classNames = ['rounded-circle', 'picture']; if (size != null) { classNames.push(`picture-${size}`); } const className = classNames.join(' '); if (user == null || !isUserObj(user)) { return ( someone ); } // determine RootElm const UserPictureSpanElm = noLink ? UserPictureRootWithoutLink : UserPictureRootWithLink; const UserPictureRootElm = noTooltip ? UserPictureSpanElm : withTooltip(UserPictureSpanElm); const userPictureSrc = user.imageUrlCached ?? DEFAULT_IMAGE; return ( {user.username} ); }); UserPicture.displayName = 'UserPicture';