1 import { useEffect, useState } from 'react';
3 import { c } from 'ttag';
5 import { LocationErrorBoundary } from '@proton/components';
6 import { useLoading } from '@proton/hooks';
7 import metrics from '@proton/metrics';
8 import { getApiError } from '@proton/shared/lib/api/helpers/apiErrorHelper';
10 import { ErrorPage, LoadingPage, PasswordPage, SharedFilePage, SharedFolderPage } from '../components/SharedPage';
11 import { useSignupFlowModal } from '../components/modals/SignupFlowModal/SignupFlowModal';
12 import { useUpsellFloatingModal } from '../components/modals/UpsellFloatingModal';
13 import usePublicToken from '../hooks/drive/usePublicToken';
14 import type { DecryptedLink } from '../store';
15 import { PublicDriveProvider, useBookmarksPublicView, useDownload, usePublicAuth, usePublicShare } from '../store';
16 import { useDriveShareURLBookmarkingFeatureFlag } from '../store/_bookmarks/useDriveShareURLBookmarking';
17 import { sendErrorReport } from '../utils/errorHandling';
18 import { getErrorMetricType } from '../utils/errorHandling/apiErrors';
19 import { Actions, countActionWithTelemetry } from '../utils/telemetry';
20 import type { ErrorTuple } from '../utils/type/ErrorTuple';
21 import { deleteStoredUrlPassword } from '../utils/url/password';
23 export default function PublicSharedLinkContainer() {
25 <LocationErrorBoundary>
27 <PublicShareLinkInitContainer />
28 </PublicDriveProvider>
29 </LocationErrorBoundary>
34 * PublicShareLinkInitContainer initiate public session for shared link.
35 * That is to initiate SRP handshake and ask for password if needed to
36 * initiate session itself.
38 function PublicShareLinkInitContainer() {
39 const { clearDownloads } = useDownload();
40 const { token, urlPassword } = usePublicToken();
44 error: [authError, authErrorMessage],
48 } = usePublicAuth(token, urlPassword);
49 const bookmarksPublicView = useBookmarksPublicView(customPassword);
50 const isDriveShareUrlBookmarkingEnabled = useDriveShareURLBookmarkingFeatureFlag();
51 const [isLoadingDecrypt, withLoading, setLoading] = useLoading(true);
52 const [[publicShareError, publicShareErrorMessage], setError] = useState<ErrorTuple>([, '']);
53 const [link, setLink] = useState<DecryptedLink>();
54 const { loadPublicShare, user, isUserLoading } = usePublicShare();
55 const [signUpFlowModal, showSignUpFlowModal] = useSignupFlowModal();
56 const [renderUpsellFloatingModal] = useUpsellFloatingModal();
58 const isLoggedIn = !!user;
59 const error: ErrorTuple[0] = authError || publicShareError;
60 const errorMessage: ErrorTuple[1] = authErrorMessage || publicShareErrorMessage;
62 // If password to the share was changed, page need to reload everything.
63 // In such case we need to also clear all downloads to not keep anything
72 // Always delete saved public share URL when browsing a public share url
73 deleteStoredUrlPassword();
78 sendErrorReport(new Error(errorMessage));
81 metrics.drive_public_share_load_error_total.increment({
82 type: !link ? 'unknown' : link.isFile ? 'file' : 'folder',
83 plan: user?.isPaid ? 'paid' : user?.isFree ? 'free' : 'not_recognized',
84 error: getErrorMetricType(error),
87 }, [errorMessage, error]);
90 const abortController = new AbortController();
92 if (token && !isLoading && !authErrorMessage && !isPasswordNeeded && !isUserLoading) {
94 loadPublicShare(abortController.signal)
97 metrics.drive_public_share_load_success_total.increment({
98 type: link.isFile ? 'file' : 'folder',
99 plan: user?.isPaid ? 'paid' : user?.isFree ? 'free' : 'not_recognized',
101 countActionWithTelemetry(Actions.PublicLinkVisit);
104 console.error(error);
105 const apiError = getApiError(error);
106 setError([error, apiError.message || error.message || c('Info').t`Cannot load shared link`]);
109 } else if (authErrorMessage) {
114 abortController.abort();
116 }, [token, isLoading, authErrorMessage, isPasswordNeeded, isUserLoading]);
119 /** If the navigation appears from a non proton user and the flag is enabled, we display a sign-up flow modal */
120 if (isDriveShareUrlBookmarkingEnabled && !isLoggedIn && !isUserLoading) {
121 showSignUpFlowModal({ urlPassword });
123 }, [isDriveShareUrlBookmarkingEnabled, isLoggedIn, isUserLoading]);
125 const showLoadingPage = isLoading || isLoadingDecrypt;
126 const showErrorPage = errorMessage || (showLoadingPage === false && link === undefined);
128 if (isPasswordNeeded) {
129 return <PasswordPage submitPassword={submitPassword} />;
132 if (showLoadingPage) {
133 return <LoadingPage />;
136 if (showErrorPage || !link) {
137 return <ErrorPage />;
144 bookmarksPublicView={bookmarksPublicView}
147 hideSaveToDrive={!isDriveShareUrlBookmarkingEnabled || isLegacy}
151 bookmarksPublicView={bookmarksPublicView}
154 hideSaveToDrive={!isDriveShareUrlBookmarkingEnabled || isLegacy}
157 {isDriveShareUrlBookmarkingEnabled && !isLoggedIn ? signUpFlowModal : renderUpsellFloatingModal}