seenByList.tsx 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import moment from 'moment';
  4. import AvatarList from 'sentry/components/avatar/avatarList';
  5. import Tooltip from 'sentry/components/tooltip';
  6. import {IconShow} from 'sentry/icons';
  7. import {t} from 'sentry/locale';
  8. import ConfigStore from 'sentry/stores/configStore';
  9. import {AvatarUser, User} from 'sentry/types';
  10. import {userDisplayName} from 'sentry/utils/formatters';
  11. type Props = {
  12. // Avatar size
  13. avatarSize?: number;
  14. className?: string;
  15. iconPosition?: 'left' | 'right';
  16. // Tooltip message for the "Seen By" icon
  17. iconTooltip?: string;
  18. // Max avatars to display
  19. maxVisibleAvatars?: number;
  20. // List of *all* users that have seen something
  21. seenBy?: User[];
  22. };
  23. const SeenByList = ({
  24. avatarSize = 28,
  25. seenBy = [],
  26. iconTooltip = t('People who have viewed this'),
  27. maxVisibleAvatars = 10,
  28. iconPosition = 'left',
  29. className,
  30. }: Props) => {
  31. const activeUser = ConfigStore.get('user');
  32. const displayUsers = seenBy.filter(user => activeUser.id !== user.id);
  33. if (displayUsers.length === 0) {
  34. return null;
  35. }
  36. // Note className="seen-by" is required for responsive design
  37. return (
  38. <SeenByWrapper iconPosition={iconPosition} className={className}>
  39. <AvatarList
  40. users={displayUsers}
  41. avatarSize={avatarSize}
  42. maxVisibleAvatars={maxVisibleAvatars}
  43. renderTooltip={user => (
  44. <Fragment>
  45. {userDisplayName(user)}
  46. <br />
  47. {moment((user as AvatarUser).lastSeen).format('LL')}
  48. </Fragment>
  49. )}
  50. />
  51. <IconWrapper iconPosition={iconPosition}>
  52. <Tooltip title={iconTooltip} skipWrapper>
  53. <IconShow size="sm" color="subText" />
  54. </Tooltip>
  55. </IconWrapper>
  56. </SeenByWrapper>
  57. );
  58. };
  59. const SeenByWrapper = styled('div')<{iconPosition: Props['iconPosition']}>`
  60. display: flex;
  61. ${p => (p.iconPosition === 'left' ? 'flex-direction: row-reverse' : '')};
  62. `;
  63. const IconWrapper = styled('div')<{iconPosition: Props['iconPosition']}>`
  64. display: flex;
  65. align-items: center;
  66. background-color: transparent;
  67. color: ${p => p.theme.textColor};
  68. height: 28px;
  69. width: 24px;
  70. text-align: center;
  71. ${p => (p.iconPosition === 'left' ? 'margin-right: 10px' : '')};
  72. `;
  73. export default SeenByList;