invite.js 3.2 KB

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