1 import { useCallback, useEffect, useMemo, useRef } from 'react';
2 import type { RouteComponentProps } from 'react-router-dom';
3 import { useLocation } from 'react-router-dom';
5 import { c } from 'ttag';
7 import { FilePreview, NavigationControl } from '@proton/components';
8 import { HTTP_STATUS_CODE } from '@proton/shared/lib/constants';
9 import { getCanAdmin } from '@proton/shared/lib/drive/permissions';
10 import { API_CUSTOM_ERROR_CODES } from '@proton/shared/lib/errors';
12 import { SignatureAlertBody } from '../components/SignatureAlert';
13 import SignatureIcon from '../components/SignatureIcon';
14 import { useDetailsModal } from '../components/modals/DetailsModal';
15 import { useLinkSharingModal } from '../components/modals/ShareLinkModal/ShareLinkModal';
16 import useIsEditEnabled from '../components/sections/useIsEditEnabled';
17 import { useActiveShare } from '../hooks/drive/useActiveShare';
18 import useNavigate from '../hooks/drive/useNavigate';
19 import { useActions, useDriveSharingFlags, useFileView } from '../store';
20 import { useOpenInDocs } from '../store/_documents';
21 // TODO: ideally not use here
22 import useSearchResults from '../store/_search/useSearchResults';
23 import { getSharedStatus } from '../utils/share';
25 export default function PreviewContainer({ match }: RouteComponentProps<{ shareId: string; linkId: string }>) {
26 const { shareId, linkId } = match.params;
30 navigateToSharedWithMe,
36 const { setFolder } = useActiveShare();
37 const [detailsModal, showDetailsModal] = useDetailsModal();
38 const [linkSharingModal, showLinkSharingModal] = useLinkSharingModal();
39 const { query: lastQuery } = useSearchResults();
40 const { isSharingInviteAvailable } = useDriveSharingFlags();
41 const { saveFile } = useActions();
43 const isEditEnabled = useIsEditEnabled();
45 const urlParams = new URLSearchParams(useLocation().search);
46 const isShareAction = urlParams.has('share');
47 const referer = urlParams.get('r');
49 !referer?.startsWith('/shared-with-me') &&
50 !referer?.startsWith('/shared-urls') &&
51 !referer?.startsWith('/trash') &&
52 !referer?.startsWith('/search');
64 } = useFileView(shareId, linkId, useNavigation);
66 const { showOpenInDocs, openInDocsAction } = useOpenInDocs(link);
68 const isAdmin = useMemo(() => getCanAdmin(permissions), [permissions]);
70 // Open sharing modal through URL parameter - needed for Proton Docs
73 showLinkSharingModal({ shareId, linkId });
77 // If the link is not type of file, probably user modified the URL.
79 if (link && !link.isFile) {
80 navigateToLink(shareId, linkId, false);
85 if (link && !referer?.startsWith('/shared-with-me')) {
86 setFolder({ shareId, linkId: link.parentLinkId });
88 }, [shareId, link?.parentLinkId]);
94 if (error.data?.Code === API_CUSTOM_ERROR_CODES.NOT_FOUND) {
97 // Block not found (storage response).
98 error.status === HTTP_STATUS_CODE.NOT_FOUND ||
99 error.data?.Code === API_CUSTOM_ERROR_CODES.INVALID_ID
105 const navigateToParent = useCallback(() => {
106 if (referer?.startsWith('/shared-with-me')) {
107 navigateToSharedWithMe();
110 if (referer?.startsWith('/shared-urls')) {
111 navigateToSharedByMe();
114 if (referer?.startsWith('/trash')) {
118 if (referer?.startsWith('/search')) {
120 navigateToSearch(lastQuery);
124 if (link?.parentLinkId) {
125 navigateToLink(shareId, link.parentLinkId, false);
127 }, [link?.parentLinkId, shareId, referer]);
129 const onOpen = useCallback(
130 (linkId: string | undefined) => {
132 navigateToLink(shareId, linkId, true);
138 const signatureStatus = useMemo(() => {
144 <SignatureIcon isFile={link.isFile} signatureIssues={link.signatureIssues} className="ml-2 color-danger" />
148 const signatureConfirmation = useMemo(() => {
149 if (!link?.signatureIssues?.blocks) {
155 signatureIssues={link.signatureIssues}
156 signatureAddress={link.signatureAddress}
163 const handleSaveFile = useCallback(
164 (content: Uint8Array[]) => {
166 return Promise.reject('missing link');
169 return saveFile(shareId, link.parentLinkId, link.name, link.mimeType, content);
171 [shareId, link?.name, link?.parentLinkId]
174 const rootRef = useRef<HTMLDivElement>(null);
179 isMetaLoading={isLinkLoading}
180 isLoading={isContentLoading}
181 error={error ? error.message || error.toString?.() || c('Info').t`Unknown error` : undefined}
183 fileName={link?.name}
184 mimeType={contentsMimeType}
185 isSharingInviteAvailable={isSharingInviteAvailable}
186 sharedStatus={getSharedStatus(link)}
187 fileSize={link?.size}
188 onClose={navigateToParent}
189 onDownload={downloadFile}
190 onSave={isEditEnabled ? handleSaveFile : undefined}
191 onDetails={() => showDetailsModal({ shareId, linkId })}
193 !isAdmin || isLinkLoading || !!link?.trashed
195 : () => showLinkSharingModal({ shareId, linkId })
200 void openInDocsAction({ shareId, linkId });
204 imgThumbnailUrl={link?.cachedThumbnailUrl}
210 current={navigation.current}
211 total={navigation.total}
213 onPrev={() => onOpen?.(navigation.prevLinkId)}
214 onNext={() => onOpen?.(navigation.nextLinkId)}
218 signatureStatus={signatureStatus}
219 signatureConfirmation={signatureConfirmation}