memberBadge.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import styled from '@emotion/styled';
  2. import omit from 'lodash/omit';
  3. import UserAvatar from 'sentry/components/avatar/userAvatar';
  4. import Link, {LinkProps} from 'sentry/components/links/link';
  5. import space from 'sentry/styles/space';
  6. import {AvatarUser, Member} from 'sentry/types';
  7. interface Props {
  8. member: Member;
  9. avatarSize?: React.ComponentProps<typeof UserAvatar>['size'];
  10. className?: string;
  11. displayEmail?: string;
  12. displayName?: React.ReactNode;
  13. hideEmail?: boolean;
  14. orgId?: string;
  15. useLink?: boolean;
  16. }
  17. function getMemberUser(member: Member): AvatarUser {
  18. if (member.user) {
  19. return member.user;
  20. }
  21. // Adapt the member into a AvatarUser
  22. return {
  23. id: '',
  24. name: member.name,
  25. email: member.email,
  26. username: '',
  27. ip_address: '',
  28. };
  29. }
  30. const MemberBadge = ({
  31. avatarSize = 24,
  32. useLink = true,
  33. hideEmail = false,
  34. displayName,
  35. displayEmail,
  36. member,
  37. orgId,
  38. className,
  39. }: Props) => {
  40. const user = getMemberUser(member);
  41. const title =
  42. displayName ||
  43. user.name ||
  44. user.email ||
  45. user.username ||
  46. user.ipAddress ||
  47. // Because this can be used to render EventUser models, or User *interface*
  48. // objects from serialized Event models. we try both ipAddress and ip_address.
  49. user.ip_address;
  50. return (
  51. <StyledUserBadge className={className}>
  52. <StyledAvatar user={user} size={avatarSize} />
  53. <StyledNameAndEmail>
  54. <StyledName
  55. useLink={useLink && !!orgId}
  56. hideEmail={hideEmail}
  57. to={(member && orgId && `/settings/${orgId}/members/${member.id}/`) || ''}
  58. >
  59. {title}
  60. </StyledName>
  61. {!hideEmail && <StyledEmail>{displayEmail || user.email}</StyledEmail>}
  62. </StyledNameAndEmail>
  63. </StyledUserBadge>
  64. );
  65. };
  66. const StyledUserBadge = styled('div')`
  67. display: flex;
  68. align-items: center;
  69. `;
  70. const StyledNameAndEmail = styled('div')`
  71. flex-shrink: 1;
  72. min-width: 0;
  73. line-height: 1;
  74. `;
  75. const StyledEmail = styled('div')`
  76. font-size: 0.875em;
  77. margin-top: ${space(0.25)};
  78. color: ${p => p.theme.gray300};
  79. ${p => p.theme.overflowEllipsis};
  80. `;
  81. interface NameProps {
  82. hideEmail: boolean;
  83. to: LinkProps['to'];
  84. useLink: boolean;
  85. }
  86. const StyledName = styled(({useLink, to, ...props}: NameProps) => {
  87. const forwardProps = omit(props, 'hideEmail');
  88. return useLink ? <Link to={to} {...forwardProps} /> : <span {...forwardProps} />;
  89. })`
  90. font-weight: ${(p: NameProps) => (p.hideEmail ? 'inherit' : 'bold')};
  91. line-height: 1.15em;
  92. ${p => p.theme.overflowEllipsis};
  93. `;
  94. const StyledAvatar = styled(UserAvatar)`
  95. min-width: ${space(3)};
  96. min-height: ${space(3)};
  97. margin-right: ${space(1)};
  98. `;
  99. export default MemberBadge;