memberBadge.tsx 2.7 KB

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