1 import { c } from 'ttag';
3 import { useNotifications } from '@proton/components';
4 import { queryResolveContextShare } from '@proton/shared/lib/api/drive/share';
5 import { API_CUSTOM_ERROR_CODES } from '@proton/shared/lib/errors';
7 import { sendErrorReport } from '../../utils/errorHandling';
8 import { useInvitationsActions } from '../_actions';
9 import { useDebouncedRequest } from '../_api';
10 import { EXTERNAL_INVITATIONS_ERROR_NAMES, useInvitations } from '../_invitations';
11 import type { DecryptedLink } from '../_links';
12 import { useLink } from '../_links';
13 import { type ShareInvitationDetails } from '../_shares';
14 import { useVolumesState } from '../_volumes';
16 export const useVolumeLinkView = () => {
17 const { getInvitationDetails, convertExternalInvitation } = useInvitations();
18 const { acceptInvitation } = useInvitationsActions();
19 const debouncedRequest = useDebouncedRequest();
20 const { getLink } = useLink();
22 const { createNotification } = useNotifications();
23 const volumeState = useVolumesState();
25 const getContextShareLinkDetails = async (
26 abortSignal: AbortSignal,
27 { volumeId, linkId }: { volumeId: string; linkId: string }
29 const { ContextShareID: contextShareId } = await debouncedRequest<{
31 ContextShareID: string;
32 }>(queryResolveContextShare({ volumeId, linkId }));
33 return getLink(abortSignal, contextShareId, linkId);
36 const handleRedirectOrAcceptInvitation = async (
37 abortSignal: AbortSignal,
47 ): Promise<{ linkId: string; shareId: string; isFile: boolean; mimeType: string } | undefined> => {
49 const invitationDetails: ShareInvitationDetails | undefined = await getInvitationDetails(abortSignal, {
53 }).catch(async (error) => {
54 if (error.data?.Code === API_CUSTOM_ERROR_CODES.NOT_FOUND) {
60 if (!invitationDetails) {
61 const link: DecryptedLink | undefined = await getContextShareLinkDetails(abortSignal, {
65 if (error.data?.Code === API_CUSTOM_ERROR_CODES.NOT_FOUND) {
71 volumeState.setVolumeShareIds(volumeId, [link.shareId]);
74 shareId: link.shareId,
76 mimeType: link.mimeType,
79 // This will happen if we can't find the invite and the file/folder does not exist
82 const acceptedInvitation = await acceptInvitation(abortSignal, invitationId, false);
83 if (!acceptedInvitation) {
87 linkId: invitationDetails.link.linkId,
88 shareId: invitationDetails.share.shareId,
89 isFile: invitationDetails.link.isFile,
90 mimeType: invitationDetails.link.mimeType,
93 // This is to make TS work with error typing
95 if (error instanceof Error) {
96 message = error.message;
99 sendErrorReport(error);
109 const handleConvertExternalInvitation = async (
110 abortSignal: AbortSignal,
112 externalInvitationId,
115 externalInvitationId: string;
119 return convertExternalInvitation(abortSignal, {
120 externalInvitationId,
124 if (result?.code === 1000) {
127 text: c('Notification').t`An invitation has been sent`,
132 text: c('Notification').t`Failed to convert external invitation`,
139 error.name !== EXTERNAL_INVITATIONS_ERROR_NAMES.DISABLED &&
140 error.name !== EXTERNAL_INVITATIONS_ERROR_NAMES.NOT_FOUND
142 sendErrorReport(error);
153 return { handleRedirectOrAcceptInvitation, handleConvertExternalInvitation };