1 import { useEffect, useState } from 'react';
3 import { c } from 'ttag';
5 import { useNotifications } from '@proton/components';
6 import { useLoading } from '@proton/hooks';
7 import { SHARE_GENERATED_PASSWORD_LENGTH } from '@proton/shared/lib/drive/constants';
8 import type { SharedURLSessionKeyPayload } from '@proton/shared/lib/interfaces/drive/sharing';
10 import { sendErrorReport } from '../../utils/errorHandling';
11 import type { DecryptedLink } from '../_links';
12 import { useLink } from '../_links';
13 import type { ShareURLLEGACY } from '../_shares';
14 import { getSharedLink, splitGeneratedAndCustomPassword } from '../_shares';
15 import useLegacyShareUrl from '../_shares/useLegacyShareUrl';
17 const getLoadingMessage = (isLinkLoading: boolean, haveShareUrl: boolean, isFile: boolean) => {
19 return c('Info').t`Loading link`;
22 return isFile ? c('Info').t`Preparing link to file` : c('Info').t`Preparing link to folder`;
25 return isFile ? c('Info').t`Creating link to file` : c('Info').t`Creating link to folder`;
28 const getConfirmationMessage = (isFile: boolean) => {
31 .t`This link will be permanently disabled. No one with this link will be able to access your file. To reshare the file, you will need a new link.`
33 .t`This link will be permanently disabled. No one with this link will be able to access your folder. To reshare the folder, you will need a new link.`;
36 const getErrorMessage = (isCreationError: boolean, error: string) => {
37 if (isCreationError) {
39 ? c('Info').t`Failed to generate a secure link. The reason is: ${error}`
40 : c('Info').t`Failed to generate a secure link. Try again later`;
42 return c('Info').t`Failed to open a secure link. The reason is: ${error}`;
45 const getSharingInfoMessage = (isFile: boolean) => {
47 ? c('Info').t`Anyone with this link can access your file.`
48 : c('Info').t`Anyone with this link can access your folder.`;
51 const getPasswordProtectedSharingInfoMessage = (isFile: boolean) => {
53 ? c('Info').t`Only the people with the link and the password can access this file.`
54 : c('Info').t`Only the people with the link and the password can access this folder.`;
58 * useLinkView loads link if not cached yet.
60 export default function useLegacyShareURLView(shareId: string, linkId: string) {
61 const { getLink } = useLink();
62 const [shareUrlInfo, setShareUrlInfo] = useState<{
63 shareUrl: ShareURLLEGACY;
64 keyInfo: SharedURLSessionKeyPayload;
66 const { loadOrCreateShareUrl, updateShareUrl, deleteShareUrl } = useLegacyShareUrl();
68 const [sharedLink, setSharedLink] = useState('');
69 const [password, setPassword] = useState('');
70 const [initialExpiration, setInitialExpiration] = useState<number | null>(null);
71 const [error, setError] = useState('');
73 const [link, setLink] = useState<DecryptedLink>();
74 const [isLinkLoading, withLinkLoading] = useLoading(true);
75 const [isShareUrlLoading, withShareUrlLoading] = useLoading(true);
76 const [isSaving, withSaving] = useLoading();
77 const [isDeleting, withDeleting] = useLoading();
78 const { createNotification } = useNotifications();
80 const shareUrl = shareUrlInfo?.shareUrl;
82 const [, customPassword] = splitGeneratedAndCustomPassword(password, shareUrl);
85 const abortController = new AbortController();
87 getLink(abortController.signal, shareId, linkId)
97 abortController.abort();
99 }, [shareId, linkId]);
101 const ShareID = shareUrl?.shareId;
103 const abortController = new AbortController();
104 void withShareUrlLoading(() => {
106 return Promise.resolve();
108 return loadOrCreateShareUrl(abortController.signal, shareId, linkId)
109 .then((shareUrlInfo) => {
110 setShareUrlInfo(shareUrlInfo);
111 setPassword(shareUrlInfo.shareUrl.password);
112 setInitialExpiration(shareUrlInfo.shareUrl.expirationTime);
113 const sharedLink = getSharedLink(shareUrlInfo.shareUrl);
115 setSharedLink(sharedLink);
124 abortController.abort();
126 }, [shareId, linkId, ShareID]);
128 const saveSharedLink = async (newCustomPassword?: string, newDuration?: number | null) => {
133 // Empty string as a newCustomPassword will remove it from the link.
134 // `undefined` is to leave the password as it is.
135 let newPassword = newCustomPassword;
136 if (newCustomPassword !== undefined && shareUrl.hasGeneratedPasswordIncluded) {
137 newPassword = password.substring(0, SHARE_GENERATED_PASSWORD_LENGTH) + newCustomPassword;
140 const update = () => {
141 const abortController = new AbortController();
142 return updateShareUrl(
143 abortController.signal,
145 shareId: shareUrl.shareId,
146 shareUrlId: shareUrl.shareUrlId,
147 flags: shareUrl.flags,
148 keyInfo: shareUrlInfo.keyInfo,
154 const updatedFields = await withSaving(update()).catch((error) => {
157 text: c('Notification').t`Your settings failed to be saved`,
162 text: c('Notification').t`Your settings have been changed successfully`,
169 keyInfo: shareUrlInfo.keyInfo,
172 if (updatedFields && updatedFields.password !== undefined) {
173 setPassword(updatedFields.password);
175 if (updatedFields && updatedFields.expirationTime !== undefined) {
176 setInitialExpiration(updatedFields.expirationTime);
179 return updatedFields;
182 const deleteLink = async () => {
183 if (!link || !shareUrl) {
188 deleteShareUrl(shareUrl.shareId, shareUrl.shareUrlId)
191 text: c('Notification').t`The link to your item was deleted`,
197 text: c('Notification').t`The link to your item failed to be deleted`,
203 const loadingMessage =
204 isLinkLoading || isShareUrlLoading
205 ? getLoadingMessage(isLinkLoading, !!link?.shareUrl, !!link?.isFile)
207 const confirmationMessage = getConfirmationMessage(!!link?.isFile);
208 const haveError = error || (!isLinkLoading && !link) || (!isShareUrlLoading && !shareUrlInfo);
209 const errorMessage = haveError ? getErrorMessage(!link?.shareUrl, error) : undefined;
210 // Show message "protected by password" only when password is saved.
211 const sharedInfoMessage = customPassword
212 ? getPasswordProtectedSharingInfoMessage(!!link?.isFile)
213 : getSharingInfoMessage(!!link?.isFile);
218 name: link?.name || '', // If the link is not loaded we will return an error message anyway
226 hasCustomPassword: !!shareUrl?.hasCustomPassword,
227 hasGeneratedPasswordIncluded: !!shareUrl?.hasGeneratedPasswordIncluded,
228 hasExpirationTime: !!shareUrl?.expirationTime,