seenByList.tsx 2.4 KB

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