invite.jsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. import { useCallback } from 'react'
  2. import PropTypes from 'prop-types'
  3. import { useShareProjectContext } from './share-project-modal'
  4. import Icon from '../../../shared/components/icon'
  5. import { Button, Col, Row } from 'react-bootstrap'
  6. import Tooltip from '../../../shared/components/tooltip'
  7. import { useTranslation } from 'react-i18next'
  8. import MemberPrivileges from './member-privileges'
  9. import { resendInvite, revokeInvite } from '../utils/api'
  10. import { useProjectContext } from '../../../shared/context/project-context'
  11. import { sendMB } from '../../../infrastructure/event-tracking'
  12. export default function Invite({ invite, isProjectOwner }) {
  13. const { t } = useTranslation()
  14. return (
  15. <Row className="project-invite">
  16. <Col xs={7}>
  17. <div>{invite.email}</div>
  18. <div className="small">
  19. {t('invite_not_accepted')}
  20. .&nbsp;
  21. {isProjectOwner && <ResendInvite invite={invite} />}
  22. </div>
  23. </Col>
  24. <Col xs={3} className="text-left">
  25. <MemberPrivileges privileges={invite.privileges} />
  26. </Col>
  27. {isProjectOwner && (
  28. <Col xs={2} className="text-center">
  29. <RevokeInvite invite={invite} />
  30. </Col>
  31. )}
  32. </Row>
  33. )
  34. }
  35. Invite.propTypes = {
  36. invite: PropTypes.object.isRequired,
  37. isProjectOwner: PropTypes.bool.isRequired,
  38. }
  39. function ResendInvite({ invite }) {
  40. const { t } = useTranslation()
  41. const { monitorRequest } = useShareProjectContext()
  42. const { _id: projectId } = useProjectContext()
  43. // const buttonRef = useRef(null)
  44. //
  45. const handleClick = useCallback(
  46. () =>
  47. monitorRequest(() => resendInvite(projectId, invite)).finally(() => {
  48. // NOTE: disabled as react-bootstrap v0.33.1 isn't forwarding the ref to the `button`
  49. // if (buttonRef.current) {
  50. // buttonRef.current.blur()
  51. // }
  52. document.activeElement.blur()
  53. }),
  54. [invite, monitorRequest, projectId]
  55. )
  56. return (
  57. <Button
  58. bsStyle="link"
  59. className="btn-inline-link"
  60. onClick={handleClick}
  61. // ref={buttonRef}
  62. >
  63. {t('resend')}
  64. </Button>
  65. )
  66. }
  67. ResendInvite.propTypes = {
  68. invite: PropTypes.object.isRequired,
  69. }
  70. function RevokeInvite({ invite }) {
  71. const { t } = useTranslation()
  72. const { updateProject, monitorRequest } = useShareProjectContext()
  73. const { _id: projectId, invites, members } = useProjectContext()
  74. function handleClick(event) {
  75. event.preventDefault()
  76. monitorRequest(() => revokeInvite(projectId, invite)).then(() => {
  77. const updatedInvites = invites.filter(existing => existing !== invite)
  78. updateProject({
  79. invites: updatedInvites,
  80. })
  81. sendMB('collaborator-invite-revoked', {
  82. project_id: projectId,
  83. current_invites_amount: updatedInvites.length,
  84. current_collaborators_amount: members.length,
  85. })
  86. })
  87. }
  88. return (
  89. <Tooltip
  90. id="revoke-invite"
  91. description={t('revoke_invite')}
  92. overlayProps={{ placement: 'bottom' }}
  93. >
  94. <Button
  95. type="button"
  96. bsStyle="link"
  97. onClick={handleClick}
  98. aria-label={t('revoke')}
  99. className="btn-inline-link"
  100. >
  101. <Icon type="times" />
  102. </Button>
  103. </Tooltip>
  104. )
  105. }
  106. RevokeInvite.propTypes = {
  107. invite: PropTypes.object.isRequired,
  108. }