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(() => {
146 signatureIssues={link.signatureIssues}
147 isAnonymous={!link.activeRevision?.signatureAddress && !link.signatureAddress}
148 className="ml-2 color-danger"
153 const signatureConfirmation = useMemo(() => {
154 if (!link?.signatureIssues?.blocks) {
160 signatureIssues={link.signatureIssues}
161 signatureAddress={link.signatureAddress}
168 const handleSaveFile = useCallback(
169 (content: Uint8Array[]) => {
171 return Promise.reject('missing link');
174 return saveFile(shareId, link.parentLinkId, link.name, link.mimeType, content);
176 [shareId, link?.name, link?.parentLinkId]
179 const rootRef = useRef<HTMLDivElement>(null);
184 isMetaLoading={isLinkLoading}
185 isLoading={isContentLoading}
186 error={error ? error.message || error.toString?.() || c('Info').t`Unknown error` : undefined}
188 fileName={link?.name}
189 mimeType={contentsMimeType}
190 isSharingInviteAvailable={isSharingInviteAvailable}
191 sharedStatus={getSharedStatus(link)}
192 fileSize={link?.size}
193 onClose={navigateToParent}
194 onDownload={downloadFile}
195 onSave={isEditEnabled ? handleSaveFile : undefined}
196 onDetails={() => showDetailsModal({ shareId, linkId })}
198 !isAdmin || isLinkLoading || !!link?.trashed
200 : () => showLinkSharingModal({ shareId, linkId })
205 void openInDocsAction({ shareId, linkId });
209 imgThumbnailUrl={link?.cachedThumbnailUrl}
215 current={navigation.current}
216 total={navigation.total}
218 onPrev={() => onOpen?.(navigation.prevLinkId)}
219 onNext={() => onOpen?.(navigation.nextLinkId)}
223 signatureStatus={signatureStatus}
224 signatureConfirmation={signatureConfirmation}