1 import type { MouseEvent } from 'react';
2 import { useMemo, useState } from 'react';
4 import { c } from 'ttag';
6 import { Button } from '@proton/atoms';
7 import type { ModalStateProps } from '@proton/components';
18 } from '@proton/components';
19 import { SHARE_MEMBER_PERMISSIONS } from '@proton/shared/lib/drive/permissions';
21 import type { ShareMember } from '../../../store';
22 import { useDriveSharingFlags, useShareMemberView, useShareURLView } from '../../../store';
23 import ModalContentLoader from '../ModalContentLoader';
24 import { DirectSharingAutocomplete, DirectSharingListing, useShareInvitees } from './DirectSharing';
25 import { DirectSharingInviteMessage } from './DirectSharing/DirectSharingInviteMessage';
26 import ErrorState from './ErrorState';
27 import { PublicSharing } from './PublicSharing';
28 import { useLinkSharingSettingsModal } from './ShareLinkSettingsModal';
29 import { ShareLinkModalLEGACY } from './_legacy/ShareLinkModalLEGACY';
32 modalTitleID?: string;
37 export function SharingModal({ shareId: rootShareId, linkId, onClose, ...modalProps }: Props & ModalStateProps) {
49 hasGeneratedPasswordIncluded,
57 } = useShareURLView(rootShareId, linkId);
70 updateMemberPermissions,
73 resendExternalInvitation,
74 updateInvitePermissions,
75 removeExternalInvitation,
76 updateExternalInvitePermissions,
78 } = useShareMemberView(rootShareId, linkId);
80 const { isDirectSharingDisabled } = useDriveSharingFlags();
82 const [settingsModal, showSettingsModal] = useLinkSharingSettingsModal();
84 const [selectedPermissions, setPermissions] = useState<SHARE_MEMBER_PERMISSIONS>(SHARE_MEMBER_PERMISSIONS.EDITOR);
85 const [inviteMessage, setInviteMessage] = useState('');
87 state: includeInviteMessage,
88 toggle: toggleIncludeInviteMessage,
89 set: setIncludeInviteMessage,
92 const isClosedButtonDisabled = isSaving || isDeleting || isCreating || isAdding;
93 // It's important in this order. As if it's hasSharedLink is true, isShared is true as well (even if cache not updated)
94 const isSharedAvailable = hasSharedLink || isShared;
95 const isSettingsDisabled = isShareUrlLoading || isSaving || isDeleting || isCreating || !isSharedAvailable;
96 const { invitees, add: addInvitee, remove: removeInvitee, clean: cleanInvitees } = useShareInvitees(existingEmails);
98 const isInvitationWorkflow = !!invitees.length;
99 const isShareWithAnyoneLoading = isShareUrlLoading || isDeleting || isCreating;
100 const isDirectSharingAutocompleteDisabled = isAdding || isLoading || isDirectSharingDisabled;
102 const cleanFields = () => {
103 setInviteMessage('');
104 setIncludeInviteMessage(true);
108 const handleSubmit = async (e: MouseEvent) => {
110 await addNewMembers({
112 permissions: selectedPermissions,
113 emailDetails: includeInviteMessage
115 message: inviteMessage,
123 // Here we check if the email address is already in invited members
124 const isSubmitDisabled = useMemo(
125 () => !invitees.length || !!invitees.find((invitee) => invitee.isLoading || invitee.error),
129 const handleCancel = () => {
133 const handlePermissionsChange = async (member: ShareMember, permissions: SHARE_MEMBER_PERMISSIONS) => {
134 await updateMemberPermissions({ ...member, permissions });
137 const handleDeleteLink = async () => {
138 await deleteLink(deleteShareIfEmpty);
141 const renderModalState = () => {
143 return <ErrorState onClose={onClose}>{errorMessage}</ErrorState>;
146 if (loadingMessage && !isShareUrlLoading) {
147 return <ModalContentLoader>{loadingMessage}</ModalContentLoader>;
151 <ContactEmailsProvider>
153 title={c('Title').t`Share ${name}`}
154 closeButtonProps={{ disabled: isClosedButtonDisabled }}
156 !isInvitationWorkflow
158 <Tooltip disabled={isSettingsDisabled} title={c('Info').t`Share via link settings`}>
166 onSaveLinkClick: saveSharedLink,
168 stopSharing: async () => {
172 havePublicSharedLink: !!sharedLink,
174 modificationDisabled: !hasGeneratedPasswordIncluded,
178 data-testid="share-modal-settings"
180 <Icon name="cog-wheel" />
187 !isInvitationWorkflow ? (
189 <DirectSharingAutocomplete
190 disabled={isDirectSharingAutocompleteDisabled}
191 existingEmails={existingEmails}
194 onRemove={removeInvitee}
195 onChangePermissions={setPermissions}
196 selectedPermissions={selectedPermissions}
198 <h2 className="text-lg text-semibold">{c('Info').t`People with access`}</h2>
203 {!isInvitationWorkflow ? (
205 <ModalTwoContent className="mb-5">
206 <DirectSharingListing
209 isLoading={isLoading}
211 invitations={invitations}
212 externalInvitations={externalInvitations}
213 onPermissionsChange={handlePermissionsChange}
214 onMemberRemove={removeMember}
215 onInvitationRemove={removeInvitation}
216 onInvitationPermissionsChange={updateInvitePermissions}
217 onExternalInvitationRemove={removeExternalInvitation}
218 onExternalInvitationPermissionsChange={updateExternalInvitePermissions}
219 onResendInvitationEmail={resendInvitation}
220 onResendExternalInvitationEmail={resendExternalInvitation}
223 {isShareUrlEnabled ? (
225 <hr className="mb-0.5 min-h-px" />
228 createSharedLink={createSharedLink}
229 isLoading={isShareWithAnyoneLoading}
230 publicSharedLink={sharedLink}
231 deleteSharedLink={handleDeleteLink}
239 <ModalTwoContent className="mb-5">
240 <DirectSharingAutocomplete
241 disabled={isDirectSharingAutocompleteDisabled}
242 existingEmails={existingEmails}
245 onRemove={removeInvitee}
246 onChangePermissions={setPermissions}
247 selectedPermissions={selectedPermissions}
249 <DirectSharingInviteMessage
251 inviteMessage={inviteMessage}
252 includeInviteMessage={includeInviteMessage}
253 onChangeInviteMessage={setInviteMessage}
254 onToggleIncludeInviteMessage={toggleIncludeInviteMessage}
258 <div className="w-full flex justify-space-between mb-4">
259 <Button disabled={isAdding} onClick={handleCancel}>{c('Action').t`Cancel`}</Button>
263 disabled={isSubmitDisabled}
265 onClick={handleSubmit}
267 {c('Action').t`Share`}
273 </ContactEmailsProvider>
282 onReset={(e: any) => {
286 disableCloseOnEscape={isSaving || isDeleting}
298 export const useLinkSharingModal = () => {
299 const { isSharingInviteAvailable } = useDriveSharingFlags();
300 return useModalTwoStatic(isSharingInviteAvailable ? SharingModal : ShareLinkModalLEGACY);