online-users-widget.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import { OnlineUser } from '@/features/ide-react/context/online-users-context'
  2. import {
  3. Dropdown,
  4. DropdownHeader,
  5. DropdownItem,
  6. DropdownMenu,
  7. DropdownToggle,
  8. } from '@/features/ui/components/bootstrap-5/dropdown-menu'
  9. import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
  10. import { getBackgroundColorForUserId } from '@/shared/utils/colors'
  11. import { useCallback, useMemo } from 'react'
  12. import { useTranslation } from 'react-i18next'
  13. // Should be kept in sync with $max-user-circles-displayed CSS constant
  14. const MAX_USER_CIRCLES_DISPLAYED = 5
  15. // We don't want a +1 circle since we could just show the user instead
  16. const MAX_USERS_WITH_OVERFLOW_VISIBLE = MAX_USER_CIRCLES_DISPLAYED - 1
  17. export const OnlineUsersWidget = ({
  18. onlineUsers,
  19. goToUser,
  20. }: {
  21. onlineUsers: OnlineUser[]
  22. goToUser: (user: OnlineUser) => void
  23. }) => {
  24. const hasOverflow = onlineUsers.length > MAX_USER_CIRCLES_DISPLAYED
  25. const usersBeforeOverflow = useMemo(
  26. () =>
  27. hasOverflow
  28. ? onlineUsers.slice(0, MAX_USERS_WITH_OVERFLOW_VISIBLE)
  29. : onlineUsers,
  30. [onlineUsers, hasOverflow]
  31. )
  32. const usersInOverflow = useMemo(
  33. () =>
  34. hasOverflow ? onlineUsers.slice(MAX_USERS_WITH_OVERFLOW_VISIBLE) : [],
  35. [onlineUsers, hasOverflow]
  36. )
  37. return (
  38. <div className="online-users-row">
  39. {usersBeforeOverflow.map((user, index) => (
  40. <OnlineUserWidget
  41. key={`${user.user_id}_${index}`}
  42. user={user}
  43. goToUser={goToUser}
  44. id={`online-user-${user.user_id}_${index}`}
  45. />
  46. ))}
  47. {hasOverflow && (
  48. <OnlineUserOverflow goToUser={goToUser} users={usersInOverflow} />
  49. )}
  50. </div>
  51. )
  52. }
  53. const OnlineUserWidget = ({
  54. user,
  55. goToUser,
  56. id,
  57. }: {
  58. user: OnlineUser
  59. goToUser: (user: OnlineUser) => void
  60. id: string
  61. }) => {
  62. const onClick = useCallback(() => {
  63. goToUser(user)
  64. }, [goToUser, user])
  65. return (
  66. <OLTooltip
  67. id={id}
  68. description={user.name}
  69. overlayProps={{
  70. placement: 'bottom',
  71. trigger: ['hover', 'focus'],
  72. delay: 0,
  73. }}
  74. >
  75. <button className="online-users-row-button" onClick={onClick}>
  76. <OnlineUserCircle user={user} />
  77. </button>
  78. </OLTooltip>
  79. )
  80. }
  81. const OnlineUserCircle = ({ user }: { user: OnlineUser }) => {
  82. const backgroundColor = getBackgroundColorForUserId(user.user_id)
  83. const [character] = [...user.name]
  84. return (
  85. <span className="online-user-circle" style={{ backgroundColor }}>
  86. {character}
  87. </span>
  88. )
  89. }
  90. const OnlineUserOverflow = ({
  91. goToUser,
  92. users,
  93. }: {
  94. goToUser: (user: OnlineUser) => void
  95. users: OnlineUser[]
  96. }) => {
  97. const { t } = useTranslation()
  98. return (
  99. <Dropdown align="end">
  100. <DropdownToggle className="online-users-row-button online-user-overflow-toggle">
  101. <OLTooltip
  102. id="connected-users"
  103. description={t('n_more_collaborators', { count: users.length })}
  104. overlayProps={{ placement: 'bottom' }}
  105. >
  106. <span className="online-user-circle">+{users.length}</span>
  107. </OLTooltip>
  108. </DropdownToggle>
  109. <DropdownMenu className="online-user-overflow-dropdown">
  110. <DropdownHeader aria-hidden="true">
  111. {t('connected_users')}
  112. </DropdownHeader>
  113. {users.map((user, index) => (
  114. <li role="none" key={`${user.user_id}_${index}`}>
  115. <DropdownItem
  116. as="button"
  117. tabIndex={-1}
  118. onClick={() => goToUser(user)}
  119. >
  120. <OnlineUserCircle user={user} /> {user.name}
  121. </DropdownItem>
  122. </li>
  123. ))}
  124. </DropdownMenu>
  125. </Dropdown>
  126. )
  127. }