share-modal-body.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import EditMember from './edit-member'
  2. import LinkSharing from './link-sharing'
  3. import Invite from './invite'
  4. import SendInvites from './send-invites'
  5. import ViewMember from './view-member'
  6. import OwnerInfo from './owner-info'
  7. import SendInvitesNotice from './send-invites-notice'
  8. import { useEditorContext } from '@/shared/context/editor-context'
  9. import { useProjectContext } from '@/shared/context/project-context'
  10. import { useMemo } from 'react'
  11. import RecaptchaConditions from '@/shared/components/recaptcha-conditions'
  12. import getMeta from '@/utils/meta'
  13. export default function ShareModalBody() {
  14. const { project, features } = useProjectContext()
  15. const { members, invites } = project || {}
  16. const { isProjectOwner } = useEditorContext()
  17. // whether the project has not reached the collaborator limit
  18. const canAddCollaborators = useMemo(() => {
  19. if (!isProjectOwner || !features) {
  20. return false
  21. }
  22. if (features.collaborators === -1) {
  23. // infinite collaborators
  24. return true
  25. }
  26. const editorInvites =
  27. invites?.filter(invite => invite.privileges !== 'readOnly').length || 0
  28. return (
  29. (members?.filter(member => member.privileges !== 'readOnly').length ||
  30. 0) +
  31. editorInvites <
  32. (features.collaborators ?? 1)
  33. )
  34. }, [members, invites, features, isProjectOwner])
  35. // determine if some but not all pending editors' permissions have been resolved,
  36. // for moving between warning and info notification states etc.
  37. const somePendingEditorsResolved = useMemo(() => {
  38. return Boolean(
  39. members?.some(member =>
  40. ['readAndWrite', 'review'].includes(member.privileges)
  41. ) &&
  42. members?.some(member => member.pendingEditor || member.pendingReviewer)
  43. )
  44. }, [members])
  45. const haveAnyEditorsBeenDowngraded = useMemo(() => {
  46. if (!isProjectOwner || !features) {
  47. return false
  48. }
  49. if (features.collaborators === -1) {
  50. return false
  51. }
  52. return (
  53. members?.some(member => member.pendingEditor || member.pendingReviewer) ||
  54. false
  55. )
  56. }, [features, isProjectOwner, members])
  57. const hasExceededCollaboratorLimit = useMemo(() => {
  58. if (!isProjectOwner || !features || !members) {
  59. return false
  60. }
  61. if (features.collaborators === -1) {
  62. return false
  63. }
  64. return (
  65. members.filter(member => member.privileges !== 'readOnly').length >
  66. (features.collaborators ?? 1)
  67. )
  68. }, [features, isProjectOwner, members])
  69. const sortedMembers = useMemo(() => {
  70. if (!members) {
  71. return []
  72. }
  73. return [
  74. ...members.filter(member => member.privileges === 'readAndWrite'),
  75. ...members.filter(member => member.pendingEditor),
  76. ...members.filter(member => member.privileges === 'review'),
  77. ...members.filter(member => member.pendingReviewer),
  78. ...members.filter(
  79. member =>
  80. !member.pendingEditor &&
  81. !member.pendingReviewer &&
  82. !['readAndWrite', 'review'].includes(member.privileges)
  83. ),
  84. ]
  85. }, [members])
  86. return (
  87. <>
  88. {isProjectOwner ? (
  89. <SendInvites
  90. canAddCollaborators={canAddCollaborators}
  91. hasExceededCollaboratorLimit={hasExceededCollaboratorLimit}
  92. haveAnyEditorsBeenDowngraded={haveAnyEditorsBeenDowngraded}
  93. somePendingEditorsResolved={somePendingEditorsResolved}
  94. />
  95. ) : (
  96. <SendInvitesNotice />
  97. )}
  98. {isProjectOwner && <LinkSharing />}
  99. <OwnerInfo />
  100. {sortedMembers.map(member =>
  101. isProjectOwner ? (
  102. <EditMember
  103. key={member._id}
  104. member={member}
  105. hasExceededCollaboratorLimit={hasExceededCollaboratorLimit}
  106. hasBeenDowngraded={Boolean(
  107. member.pendingEditor || member.pendingReviewer
  108. )}
  109. canAddCollaborators={canAddCollaborators}
  110. isReviewerOnFreeProject={
  111. member.privileges === 'review' && !features.trackChanges
  112. }
  113. />
  114. ) : (
  115. <ViewMember key={member._id} member={member} />
  116. )
  117. )}
  118. {(invites || []).map(invite => (
  119. <Invite
  120. key={invite._id}
  121. invite={invite}
  122. isProjectOwner={isProjectOwner}
  123. />
  124. ))}
  125. {!getMeta('ol-ExposedSettings').recaptchaDisabled?.invite && (
  126. <RecaptchaConditions />
  127. )}
  128. </>
  129. )
  130. }