online-users-widget.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import React from 'react'
  2. import { useTranslation } from 'react-i18next'
  3. import {
  4. Dropdown,
  5. DropdownHeader,
  6. DropdownItem,
  7. DropdownMenu,
  8. DropdownToggle,
  9. } from '@/features/ui/components/bootstrap-5/dropdown-menu'
  10. import { getBackgroundColorForUserId } from '@/shared/utils/colors'
  11. import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
  12. import MaterialIcon from '@/shared/components/material-icon'
  13. import { OnlineUser } from '@/features/ide-react/context/online-users-context'
  14. function OnlineUsersWidget({
  15. onlineUsers,
  16. goToUser,
  17. }: {
  18. onlineUsers: OnlineUser[]
  19. goToUser: (user: OnlineUser) => void
  20. }) {
  21. const { t } = useTranslation()
  22. const shouldDisplayDropdown = onlineUsers.length >= 4
  23. if (shouldDisplayDropdown) {
  24. return (
  25. <Dropdown className="online-users" align="end">
  26. <DropdownToggle
  27. id="online-users"
  28. as={DropDownToggleButton}
  29. // @ts-ignore: fix type of DropdownToggle with "as" prop so that it can accept
  30. // custom props for that component
  31. onlineUserCount={onlineUsers.length}
  32. />
  33. <DropdownMenu>
  34. <DropdownHeader aria-hidden="true">
  35. {t('connected_users')}
  36. </DropdownHeader>
  37. {onlineUsers.map((user, index) => (
  38. <li role="none" key={`${user.user_id}_${index}`}>
  39. <DropdownItem
  40. as="button"
  41. tabIndex={-1}
  42. onClick={() => goToUser(user)}
  43. >
  44. <UserIcon user={user} showName />
  45. </DropdownItem>
  46. </li>
  47. ))}
  48. </DropdownMenu>
  49. </Dropdown>
  50. )
  51. } else {
  52. return (
  53. <div className="online-users">
  54. {onlineUsers.map((user, index) => (
  55. <OLTooltip
  56. key={`${user.user_id}_${index}`}
  57. id="online-user"
  58. description={user.name}
  59. overlayProps={{ placement: 'bottom', trigger: ['hover', 'focus'] }}
  60. tooltipProps={{ translate: 'no' }}
  61. >
  62. <span>
  63. {/* OverlayTrigger won't fire unless UserIcon is wrapped in a span */}
  64. <UserIcon user={user} onClick={goToUser} />
  65. </span>
  66. </OLTooltip>
  67. ))}
  68. </div>
  69. )
  70. }
  71. }
  72. function UserIcon({
  73. user,
  74. showName,
  75. onClick,
  76. }: {
  77. user: OnlineUser
  78. showName?: boolean
  79. onClick?: (user: OnlineUser) => void
  80. }) {
  81. const backgroundColor = getBackgroundColorForUserId(user.user_id)
  82. function handleOnClick() {
  83. onClick?.(user)
  84. }
  85. const [character] = [...user.name]
  86. return (
  87. // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
  88. <span onClick={handleOnClick}>
  89. <span className="online-user" style={{ backgroundColor }}>
  90. {character}
  91. </span>
  92. {showName && user.name}
  93. </span>
  94. )
  95. }
  96. const DropDownToggleButton = React.forwardRef<
  97. HTMLButtonElement,
  98. { onlineUserCount: number; onClick: React.MouseEventHandler }
  99. >((props, ref) => {
  100. const { t } = useTranslation()
  101. return (
  102. <OLTooltip
  103. id="connected-users"
  104. description={t('connected_users')}
  105. overlayProps={{ placement: 'left' }}
  106. >
  107. <button
  108. type="button"
  109. className="online-user online-user-multi"
  110. onClick={props.onClick} // required by Bootstrap Dropdown to trigger an opening
  111. ref={ref}
  112. >
  113. <strong>{props.onlineUserCount}</strong>&nbsp;
  114. <MaterialIcon type="groups" />
  115. </button>
  116. </OLTooltip>
  117. )
  118. })
  119. DropDownToggleButton.displayName = 'DropDownToggleButton'
  120. export default OnlineUsersWidget