Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / applications / drive / src / app / containers / FolderContainer.tsx
blob90b8582c21df08a8487580dac16e7a588afa4486
1 import { useEffect, useMemo, useRef, useState } from 'react';
2 import type { RouteComponentProps } from 'react-router';
3 import { Route } from 'react-router';
5 import { Loader, useAppTitle } from '@proton/components';
6 import useLoading from '@proton/hooks/useLoading';
7 import { LinkURLType } from '@proton/shared/lib/drive/constants';
8 import { API_CUSTOM_ERROR_CODES } from '@proton/shared/lib/errors';
9 import noop from '@proton/utils/noop';
11 import DriveStartupModals from '../components/modals/DriveStartupModals';
12 import type { DriveSectionRouteProps } from '../components/sections/Drive/DriveView';
13 import DriveView from '../components/sections/Drive/DriveView';
14 import type { DriveFolder } from '../hooks/drive/useActiveShare';
15 import { useActiveShare } from '../hooks/drive/useActiveShare';
16 import { useFolderContainerTitle } from '../hooks/drive/useFolderContainerTitle';
17 import useNavigate from '../hooks/drive/useNavigate';
18 import { useContextShareHandler, useDefaultShare, useDriveEventManager } from '../store';
19 import { VolumeType, useVolumesState } from '../store/_volumes';
20 import PreviewContainer from './PreviewContainer';
22 const hasValidLinkType = (type: string) => {
23     return type === LinkURLType.FILE || type === LinkURLType.FOLDER;
26 export default function FolderContainer({ match }: RouteComponentProps<DriveSectionRouteProps>) {
27     const { navigateToRoot, navigateToNoAccess } = useNavigate();
28     const { activeFolder, setFolder } = useActiveShare();
29     const volumesState = useVolumesState();
30     const lastFolderPromise = useRef<Promise<DriveFolder | undefined>>();
31     const [, setError] = useState();
32     const { getDefaultShare, isShareAvailable } = useDefaultShare();
33     const driveEventManager = useDriveEventManager();
35     useFolderContainerTitle({ params: match.params, setAppTitle: useAppTitle });
37     const folderPromise = useMemo(async () => {
38         const { shareId, type, linkId } = match.params;
40         if (!shareId && !type && !linkId) {
41             const defaultShare = await getDefaultShare();
42             if (defaultShare) {
43                 return { shareId: defaultShare.shareId, linkId: defaultShare.rootLinkId };
44             }
45             setError(() => {
46                 // Throwing error in async function does not propagate it to
47                 // the view. Therefore we need to use this setError hack.
48                 throw new Error('Drive is not initilized, cache has been cleared unexpectedly');
49             });
50         } else if (!shareId || !hasValidLinkType(type as string) || !linkId) {
51             console.warn('Missing parameters, should be none or shareId/type/linkId');
52             navigateToRoot();
53         } else if (type === LinkURLType.FOLDER) {
54             const ac = new AbortController();
55             const isAvailable = await isShareAvailable(ac.signal, shareId);
56             if (!isAvailable) {
57                 console.warn('Provided share is not available, probably locked or soft deleted');
58                 navigateToRoot();
59                 return;
60             }
61             return { shareId, linkId };
62         }
63         return lastFolderPromise.current;
64     }, [match.params.shareId, match.params.type, match.params.linkId]);
66     useEffect(() => {
67         folderPromise
68             .then((folder) => {
69                 if (folder) {
70                     setFolder(folder);
71                 }
72             })
73             .catch((err) => {
74                 console.warn(err);
75                 if (err.data?.Code === API_CUSTOM_ERROR_CODES.NOT_FOUND) {
76                     navigateToNoAccess();
77                 } else {
78                     navigateToRoot();
79                 }
80             });
81     }, [folderPromise]);
83     // With sharing we need to subscribe to events from different volumes.
84     // This will happen during Shared with me navigation
85     useEffect(() => {
86         const volumeId = volumesState.findVolumeId(activeFolder.shareId);
87         if (!volumeId) {
88             return;
89         }
90         getDefaultShare()
91             .then((defaultShare) => {
92                 // We exclude subscribing to volumes event of main share (Already done in MainContainer)
93                 if (defaultShare.volumeId !== volumeId) {
94                     driveEventManager.volumes.startSubscription(volumeId, VolumeType.shared).catch(noop);
95                 }
96             })
97             .catch(noop);
99         return () => {
100             // This is memoized so should not make another call
101             void getDefaultShare().then((defaultShare) => {
102                 // We exclude pausing subscription to volumes event of main share
103                 if (defaultShare.volumeId !== volumeId) {
104                     driveEventManager.volumes.pauseSubscription(volumeId);
105                 }
106             });
107         };
108     }, [activeFolder.shareId]);
110     // In case we open preview, folder doesn't need to change.
111     lastFolderPromise.current = folderPromise;
113     const shouldRenderDriveView = Boolean(activeFolder.shareId && activeFolder.linkId);
115     return (
116         <>
117             {shouldRenderDriveView ? <DriveView /> : null}
118             <DriveStartupModals />
119             <Route path={`/:shareId?/${LinkURLType.FILE}/:linkId?`} component={PreviewContainer} exact />
120         </>
121     );
124 export const FolderConntainerWrapper = ({ match, ...props }: RouteComponentProps<DriveSectionRouteProps>) => {
125     const { isShareAvailable } = useDefaultShare();
126     const [isLoading, withLoading] = useLoading(true);
127     const { navigateToRoot } = useNavigate();
128     const { handleContextShare } = useContextShareHandler();
130     useEffect(() => {
131         const abortController = new AbortController();
132         void withLoading(async () => {
133             const { shareId, type, linkId } = match.params;
134             if (!shareId || !linkId || !type || !hasValidLinkType(type)) {
135                 return;
136             }
137             await isShareAvailable(abortController.signal, shareId).catch(async (err) => {
138                 if (err.data?.Code === API_CUSTOM_ERROR_CODES.NOT_ALLOWED) {
139                     await handleContextShare(abortController.signal, {
140                         shareId,
141                         linkId,
142                         isFile: type === LinkURLType.FILE,
143                     });
144                     return;
145                 }
146                 console.warn('Provided share is not available, probably locked or soft deleted');
147                 navigateToRoot();
148                 return;
149             });
150         });
151         return () => {
152             abortController.abort();
153         };
154     }, [match.params.shareId, match.params.type, match.params.linkId]);
156     if (isLoading) {
157         return <Loader size="medium" className="absolute inset-center" />;
158     }
159     return <FolderContainer match={match} {...props} />;