Files
workavia-drive/tdrive/frontend/src/app/features/drive/hooks/use-drive-actions.tsx
T
2024-11-27 12:51:06 +01:00

325 lines
10 KiB
TypeScript

import { ToasterService } from '@features/global/services/toaster-service';
import useRouterCompany from '@features/router/hooks/use-router-company';
import { useCallback } from 'react';
import { useRecoilValue, useRecoilCallback, useRecoilState } from 'recoil';
import { DriveApiClient } from '../api-client/api-client';
import {
DriveItemAtom,
DriveItemChildrenAtom,
DriveItemPagination,
DriveItemSort,
} from '../state/store';
import { BrowseFilter, DriveItem, DriveItemVersion } from '../types';
import { SharedWithMeFilterState } from '../state/shared-with-me-filter';
import Languages from 'features/global/services/languages-service';
import { useUserQuota } from 'features/users/hooks/use-user-quota';
import AlertManager from 'app/features/global/services/alert-manager-service';
import FeatureTogglesService, {
FeatureNames,
} from '@features/global/services/feature-toggles-service';
import Logger from '@features/global/framework/logger-service';
/**
* Returns the children of a drive item
* @returns
*/
export const useDriveActions = (inPublicSharing?: boolean) => {
const companyId = useRouterCompany();
const sharedFilter = useRecoilValue(SharedWithMeFilterState);
const sortItem = useRecoilValue(DriveItemSort);
const [paginateItem] = useRecoilState(DriveItemPagination);
const { getQuota } = useUserQuota();
const AVEnabled = FeatureTogglesService.isActiveFeatureName(FeatureNames.COMPANY_AV_ENABLED);
/**
* Downloads a file from the given URL, ensuring compatibility across all browsers, including Safari.
*
* @param fileUrl - The URL of the file to download.
*/
const downloadFile = (fileUrl: string) => {
try {
// Attempt to open the URL in a new tab
const popupWindow = window.open(fileUrl, '_blank');
if (popupWindow) {
popupWindow.focus();
} else {
// Fallback for Safari or blocked pop-ups
const downloadLink = document.createElement('a');
downloadLink.href = fileUrl;
downloadLink.download = ''; // Suggests a download instead of navigation
downloadLink.target = '_self'; // Ensures it opens in the same tab
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
} catch (error) {
Logger.error('Error during file download:', error);
}
};
const refresh = useRecoilCallback(
({ set, snapshot }) =>
async (parentId: string, resetPagination?: boolean) => {
if (parentId) {
const filter: BrowseFilter = {
company_id: companyId,
mime_type: sharedFilter.mimeType.value,
};
let pagination = await snapshot.getPromise(DriveItemPagination);
if (resetPagination) {
pagination = { page: 0, limit: pagination.limit };
set(DriveItemPagination, pagination);
}
try {
const details = await DriveApiClient.browse(
companyId,
parentId,
filter,
sortItem,
pagination,
);
set(DriveItemChildrenAtom(parentId), details.children);
set(DriveItemAtom(parentId), details);
for (const child of details.children) {
const currentValue = snapshot.getLoadable(DriveItemAtom(child.id)).contents;
if (!currentValue) {
//only update if not already in cache to avoid concurrent updates
set(DriveItemAtom(child.id), { item: child });
}
}
return details;
} catch (e) {
ToasterService.error(Languages.t('hooks.use-drive-actions.unable_load_file'));
} finally {
set(DriveItemPagination, { page: pagination.limit, limit: pagination.limit });
}
}
},
[companyId, sortItem],
);
const create = useCallback(
async (item: Partial<DriveItem>, version: Partial<DriveItemVersion>) => {
if (!item || !version) throw new Error('All ');
if (!item.company_id) item.company_id = companyId;
try {
const driveFile = await DriveApiClient.create(companyId, { item, version });
await refresh(driveFile.parent_id, true);
await getQuota();
return driveFile;
} catch (e: any) {
if (e.statusCode === 403) {
ToasterService.info(
<>
<p>{Languages.t('hooks.use-drive-actions.quota_limit_exceeded_title')}</p>
<p>{Languages.t('hooks.use-drive-actions.quota_limit_exceeded_message')}</p>
<p>{Languages.t('hooks.use-drive-actions.quota_limit_exceeded_plans')}</p>
</>,
);
} else {
ToasterService.error(Languages.t('hooks.use-drive-actions.unable_create_file'));
}
return null;
}
},
[refresh],
);
const download = useCallback(
async (id: string, isMalicious = false, versionId?: string) => {
try {
const url = DriveApiClient.getDownloadUrl(companyId, id, versionId);
// if AV is enabled
if (AVEnabled) {
// if the file is malicious
if (isMalicious) {
// toggle confirm for user
AlertManager.confirm(
() => {
downloadFile(url);
},
() => {
return;
},
{
text: Languages.t('hooks.use-drive-actions.av_confirm_file_download'),
},
);
} else {
downloadFile(url);
}
} else {
downloadFile(url);
}
} catch (e) {
ToasterService.error(Languages.t('hooks.use-drive-actions.unable_download_file'));
}
},
[companyId],
);
const downloadZip = useCallback(
async (ids: string[], isDirectory = false, containsMalicious = false) => {
try {
const triggerDownload = async () => {
const url = await DriveApiClient.getDownloadZipUrl(companyId, ids, isDirectory);
downloadFile(url);
};
if (AVEnabled) {
const containsMaliciousFiles =
containsMalicious ||
(ids.length === 1 && (await DriveApiClient.checkMalware(companyId, ids[0])));
if (containsMaliciousFiles) {
AlertManager.confirm(
async () => {
await triggerDownload();
},
() => {
return;
},
{
text: Languages.t('hooks.use-drive-actions.av_confirm_folder_download'),
},
);
} else {
await triggerDownload();
}
} else {
await triggerDownload();
}
} catch (e) {
ToasterService.error(Languages.t('hooks.use-drive-actions.unable_download_file'));
}
},
[companyId],
);
const remove = useCallback(
async (id: string | string[], parentId: string) => {
try {
if (Array.isArray(id)) {
await Promise.all(id.map(i => DriveApiClient.remove(companyId, i)));
} else await DriveApiClient.remove(companyId, id);
await refresh(parentId || '', true);
await getQuota();
} catch (e) {
ToasterService.error(Languages.t('hooks.use-drive-actions.unable_remove_file'));
}
},
[refresh],
);
const restore = useCallback(
async (id: string, parentId: string) => {
try {
await DriveApiClient.restore(companyId, id);
await refresh(parentId || '', true);
} catch (e) {
ToasterService.error(Languages.t('hooks.use-drive-actions.unable_restore_file'));
}
},
[refresh],
);
const update = useCallback(
async (update: Partial<DriveItem>, id: string, parentId: string, previousName?: string) => {
try {
const newItem = await DriveApiClient.update(companyId, id, update);
if (previousName && previousName !== newItem.name && !update.name)
ToasterService.warn(
Languages.t('hooks.use-drive-actions.update_caused_a_rename', [
previousName,
newItem.name,
]),
);
await refresh(id || '', true);
if (!inPublicSharing) await refresh(parentId || '', true);
if (update?.parent_id !== parentId) await refresh(update?.parent_id || '', true);
} catch (e) {
ToasterService.error(Languages.t('hooks.use-drive-actions.unable_update_file'));
}
},
[refresh],
);
const updateLevel = useCallback(
async (id: string, userId: string, level: string) => {
try {
const updateBody = {
company_id: companyId,
user_id: userId,
level: level,
};
await DriveApiClient.updateLevel(companyId, id, updateBody);
await refresh(id || '', true);
} catch (e) {
ToasterService.error(Languages.t('hooks.use-drive-actions.unable_update_file'));
}
},
[refresh],
);
const nextPage = useRecoilCallback(
({ snapshot }) =>
async (parentId: string) => {
const filter: BrowseFilter = {
company_id: companyId,
mime_type: sharedFilter.mimeType.value,
};
const pagination = await snapshot.getPromise(DriveItemPagination);
const details = await DriveApiClient.browse(
companyId,
parentId,
filter,
sortItem,
pagination,
);
return details;
},
[paginateItem, refresh],
);
const checkMalware = useCallback(
async (item: Partial<DriveItem>) => {
try {
await DriveApiClient.checkMalware(companyId, item.id || '');
} catch (e) {
ToasterService.error(Languages.t('hooks.use-drive-actions.unable_rescan_file'));
}
},
[refresh],
);
const reScan = useCallback(
async (item: Partial<DriveItem>) => {
try {
await DriveApiClient.reScan(companyId, item.id || '');
await refresh(item.parent_id || '', true);
} catch (e) {
ToasterService.error(Languages.t('hooks.use-drive-actions.unable_rescan_file'));
}
},
[refresh],
);
return {
create,
refresh,
download,
downloadZip,
remove,
restore,
update,
updateLevel,
reScan,
checkMalware,
nextPage,
};
};