overleaf/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/share-modal-body.tsx
roo hutton 2dcf87e3f6 [web] Share modal shows downgraded editors (#20015)
* add hasBeenDowngraded prop for EditMember

* reduce padding on share modal collab row, add prompt to hasBeenDowngraded Select

* share modal select styling tweaks to allow for inline warning icon

* always show editor limit subtitle when in downgraded state

* add AccessLevelsChanged warning, tweak owner row styling

* conditionally set hasBeenDowngraded prop. make invited member row styling more consistent between warning/enforcement

* add an info state for access level changed notification

* add notification for lost edit access on collaborator share modal, TSify SendInvitesNotice

* fix member privilege alignment in collaborator share modal

* show blue upgrade CTA when some pending editors have been resolved

* automatically show share modal to owners when has pending editors or is over collab limit

* only show lost edit access warning in read-only share modal to pending editors

---------

Co-authored-by: Thomas <thomas-@users.noreply.github.com>
GitOrigin-RevId: e3b88052a48b8f598299ffc55b7c24cb793da151
2024-08-27 08:04:49 +00:00

118 lines
3.4 KiB
TypeScript

import EditMember from './edit-member'
import LinkSharing from './link-sharing'
import Invite from './invite'
import SendInvites from './send-invites'
import ViewMember from './view-member'
import OwnerInfo from './owner-info'
import SendInvitesNotice from './send-invites-notice'
import { useEditorContext } from '@/shared/context/editor-context'
import { useProjectContext } from '@/shared/context/project-context'
import { useMemo } from 'react'
import RecaptchaConditions from '@/shared/components/recaptcha-conditions'
import getMeta from '@/utils/meta'
export default function ShareModalBody() {
const { members, invites, features } = useProjectContext()
const { isProjectOwner } = useEditorContext()
// whether the project has not reached the collaborator limit
const canAddCollaborators = useMemo(() => {
if (!isProjectOwner || !features) {
return false
}
if (features.collaborators === -1) {
// infinite collaborators
return true
}
const editorInvites = invites.filter(
invite => invite.privileges === 'readAndWrite'
).length
return (
members.filter(member => member.privileges === 'readAndWrite').length +
editorInvites <
(features.collaborators ?? 1)
)
}, [members, invites, features, isProjectOwner])
// determine if some but not all pending editors' permissions have been resolved,
// for moving between warning and info notification states etc.
const somePendingEditorsResolved = useMemo(() => {
return (
members.some(member => member.privileges === 'readAndWrite') &&
members.some(member => member.pendingEditor)
)
}, [members])
const haveAnyEditorsBeenDowngraded = useMemo(() => {
if (!isProjectOwner || !features) {
return false
}
if (features.collaborators === -1) {
return false
}
return members.some(member => member.pendingEditor)
}, [features, isProjectOwner, members])
const hasExceededCollaboratorLimit = useMemo(() => {
if (!isProjectOwner || !features) {
return false
}
if (features.collaborators === -1) {
return false
}
return (
members.filter(member => member.privileges === 'readAndWrite').length >
(features.collaborators ?? 1)
)
}, [features, isProjectOwner, members])
return (
<>
{isProjectOwner ? (
<SendInvites
canAddCollaborators={canAddCollaborators}
hasExceededCollaboratorLimit={hasExceededCollaboratorLimit}
haveAnyEditorsBeenDowngraded={haveAnyEditorsBeenDowngraded}
somePendingEditorsResolved={somePendingEditorsResolved}
/>
) : (
<SendInvitesNotice />
)}
{isProjectOwner && <LinkSharing />}
<OwnerInfo />
{members.map(member =>
isProjectOwner ? (
<EditMember
key={member._id}
member={member}
hasExceededCollaboratorLimit={hasExceededCollaboratorLimit}
hasBeenDowngraded={member.pendingEditor ?? false}
canAddCollaborators={canAddCollaborators}
/>
) : (
<ViewMember key={member._id} member={member} />
)
)}
{invites.map(invite => (
<Invite
key={invite._id}
invite={invite}
isProjectOwner={isProjectOwner}
/>
))}
{!getMeta('ol-ExposedSettings').recaptchaDisabled?.invite && (
<RecaptchaConditions />
)}
</>
)
}