📁 Changed TDrive root folder (#16)

📁 Changed TDrive root folder
This commit is contained in:
Montassar Ghanmy
2023-04-11 10:04:29 +01:00
committed by GitHub
parent 3b3f67af07
commit e0615fa867
10548 changed files with 48 additions and 48 deletions
@@ -0,0 +1,107 @@
import { ToasterService } from 'app/features/global/services/toaster-service';
import useRouterCompany from 'app/features/router/hooks/use-router-company';
import { useCallback } from 'react';
import { useRecoilCallback } from 'recoil';
import { DriveApiClient } from '../api-client/api-client';
import { DriveItemAtom, DriveItemChildrenAtom } from '../state/store';
import { DriveItem, DriveItemVersion } from '../types';
/**
* Returns the children of a drive item
* @param id
* @returns
*/
export const useDriveActions = () => {
const companyId = useRouterCompany();
const refresh = useRecoilCallback(
({ set, snapshot }) =>
async (parentId: string) => {
if (parentId) {
try {
const details = await DriveApiClient.get(companyId, parentId);
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('Unable to load your files.');
}
}
},
[companyId],
);
const create = useCallback(
async (item: Partial<DriveItem>, version: Partial<DriveItemVersion>) => {
let driveFile = null;
if (!item.company_id) item.company_id = companyId;
try {
driveFile = await DriveApiClient.create(companyId, { item, version });
await refresh(item.parent_id!);
} catch (e) {
ToasterService.error('Unable to create a new file.');
}
return driveFile;
},
[refresh],
);
const download = useCallback(
async (id: string, versionId?: string) => {
try {
const url = await DriveApiClient.getDownloadUrl(companyId, id, versionId);
(window as any).open(url, '_blank').focus();
} catch (e) {
ToasterService.error('Unable to download this file.');
}
},
[companyId],
);
const downloadZip = useCallback(
async (ids: string[]) => {
try {
const url = await DriveApiClient.getDownloadZipUrl(companyId, ids);
(window as any).open(url, '_blank').focus();
} catch (e) {
ToasterService.error('Unable to download this files.');
}
},
[companyId],
);
const remove = useCallback(
async (id: string, parentId: string) => {
try {
await DriveApiClient.remove(companyId, id);
await refresh(parentId || '');
} catch (e) {
ToasterService.error('Unable to remove this file.');
}
},
[refresh],
);
const update = useCallback(
async (update: Partial<DriveItem>, id: string, parentId: string) => {
try {
await DriveApiClient.update(companyId, id, update);
await refresh(id || '');
await refresh(parentId || '');
if (update?.parent_id !== parentId) await refresh(update?.parent_id || '');
} catch (e) {
ToasterService.error('Unable to update this file.');
}
},
[refresh],
);
return { create, refresh, download, downloadZip, remove, update };
};
@@ -0,0 +1,106 @@
import { ToasterService } from 'app/features/global/services/toaster-service';
import { LoadingStateInitTrue } from 'app/features/global/state/atoms/Loading';
import useRouterCompany from 'app/features/router/hooks/use-router-company';
import { useCallback, useRef } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { DriveItemAtom, DriveItemChildrenAtom } from '../state/store';
import { DriveItem } from '../types';
import { useDriveActions } from './use-drive-actions';
import { useDriveUpload } from './use-drive-upload';
import short from 'short-uuid';
/**
* Get in store single item and expose methods to operate on it
* @param id
* @returns
*/
export const useDriveItem = (id: string) => {
const companyId = useRouterCompany();
const item = useRecoilValue(DriveItemAtom(id));
const children = useRecoilValue(DriveItemChildrenAtom(id));
const [loading, setLoading] = useRecoilState(LoadingStateInitTrue('useDriveItem-' + id));
const { refresh: refreshItem, create, update: _update, remove: _remove } = useDriveActions();
const { uploadVersion: _uploadVersion } = useDriveUpload();
const refresh = useCallback(
async (parentId: string) => {
setLoading(true);
try {
await refreshItem(parentId);
} finally {
setLoading(false);
}
},
[setLoading, refreshItem],
);
const remove = useCallback(async () => {
setLoading(true);
try {
await _remove(id, item?.item?.parent_id || '');
} catch (e) {
ToasterService.error('Unable to remove this file.');
}
setLoading(false);
}, [id, setLoading, refresh, item?.item?.parent_id]);
const update = useCallback(
async (update: Partial<DriveItem>) => {
setLoading(true);
try {
await _update(update, id, item?.item?.parent_id || '');
} catch (e) {
ToasterService.error('Unable to update this file.');
}
setLoading(false);
},
[id, setLoading, refresh, item?.item?.parent_id],
);
const uploadVersion = useCallback(
async (file: File) => {
setLoading(true);
try {
await _uploadVersion(file, { companyId, id });
} catch (e) {
ToasterService.error('Unable to create a new version of this file.');
}
setLoading(false);
},
[companyId, id, setLoading, refresh, item?.item?.parent_id],
);
const inTrash = id === 'trash' || item?.path?.some(i => i.parent_id === 'trash');
return {
inTrash,
loading: loading,
children: children || [],
details: item,
path: item?.path,
item: item?.item,
access: item?.access,
websockets: item?.websockets,
versions: item?.versions,
uploadVersion,
create,
update,
remove,
refresh,
};
};
const translator = short();
export const getPublicLink = (item?: DriveItem): string => {
let publicLink = `${document.location.protocol}//${document.location.host}`;
try {
publicLink +=
`/shared/${translator.fromUUID(item?.company_id || '')}` +
`/drive/${translator.fromUUID(item?.id || '')}` +
`/t/${item?.access_info?.public?.token}`;
} catch (e) {
return publicLink;
}
return publicLink;
};
@@ -0,0 +1,83 @@
import fileUploadApiClient from 'app/features/files/api/file-upload-api-client';
import fileUploadService from 'app/features/files/services/file-upload-service';
import { useGlobalEffect } from 'app/features/global/hooks/use-global-effect';
import { LoadingState } from 'app/features/global/state/atoms/Loading';
import { useRecoilState } from 'recoil';
import { DriveApiClient } from '../api-client/api-client';
import { DriveViewerState } from '../state/viewer';
import { DriveItem } from '../types';
export const useDrivePreviewModal = () => {
const [status, setStatus] = useRecoilState(DriveViewerState);
const open: (item: DriveItem) => void = (item: DriveItem) => {
if (item.last_version_cache?.file_metadata?.source === 'internal') {
setStatus({ item, loading: true });
}
};
const close = () => setStatus({ item: null, loading: true });
return { open, close, isOpen: !!status.item };
};
export const useDrivePreview = () => {
const [status, setStatus] = useRecoilState(DriveViewerState);
const modal = useDrivePreviewModal();
useGlobalEffect(
'useDrivePreview',
async () => {
if (modal.isOpen && status.item) {
setStatus({
...status,
loading: true,
});
const details = await DriveApiClient.get(status.item.company_id, status.item.id);
setStatus({
...status,
details,
loading: false,
});
}
},
[status.item?.id],
);
return {
...modal,
status,
loading: status.loading,
};
};
export const useDrivePreviewLoading = () => {
const [loading, setLoading] = useRecoilState(LoadingState('useDrivePreviewLoading'));
return { loading, setLoading };
};
export const useDrivePreviewDisplayData = () => {
const { status } = useDrivePreview();
if (!status) {
return {};
}
const name =
status.details?.item.last_version_cache.file_metadata.name || status.details?.item.name || '';
const extension = name.split('.').pop();
const type = fileUploadApiClient.mimeToType(
status.details?.item.last_version_cache.file_metadata.mime || '',
extension,
);
const id = status.details?.item.last_version_cache.file_metadata.external_id || '';
const download = fileUploadService.getDownloadRoute({
companyId: status.item?.company_id || '',
fileId: status.details?.item.last_version_cache.file_metadata.external_id || '',
});
return { download, id, name, type, extension, size: status.details?.item.size };
};
@@ -0,0 +1,17 @@
import { useRealtimeRoom } from 'app/features/global/hooks/use-realtime';
import { useDriveActions } from './use-drive-actions';
import { useDriveItem } from './use-drive-item';
export const useDriveRealtime = (id: string) => {
const { refresh } = useDriveActions();
const { websockets } = useDriveItem(id);
const room = websockets?.[0];
useRealtimeRoom(room as { room: string; token: string }, 'useDriveRealtime-' + id, () => {
refresh(id);
});
};
export const DriveRealtimeObject = ({ id }: { id: string }) => {
useDriveRealtime(id);
return <></>;
};
@@ -0,0 +1,163 @@
import { FileTreeObject } from 'app/components/uploads/file-tree-utils';
import FileUploadService from 'app/features/files/services/file-upload-service';
import { ToasterService } from 'app/features/global/services/toaster-service';
import { DriveApiClient } from '../api-client/api-client';
import { useDriveActions } from './use-drive-actions';
/**
* Returns the children of a drive item
* @param id
* @returns
*/
export const useDriveUpload = () => {
const { create } = useDriveActions();
const uploadVersion = async (file: File, context: { companyId: string; id: string }) => {
return new Promise(r => {
FileUploadService.upload([file], {
context: {
companyId: context.companyId,
id: context.id,
},
callback: async (file, context) => {
if (file) {
const version = {
drive_item_id: context.id,
provider: 'internal',
file_metadata: {
name: file.metadata?.name,
size: file.upload_data?.size,
mime: file.metadata?.mime,
thumbnails: file?.thumbnails,
source: 'internal',
external_id: file.id,
},
};
await DriveApiClient.createVersion(context.companyId, context.id, version);
}
r(true);
},
});
});
};
const uploadTree = async (
tree: FileTreeObject,
context: { companyId: string; parentId: string },
) => {
const filesPerParentId: { [key: string]: File[] } = {};
// Create all directories
const createDirectories = async (tree: FileTreeObject['tree'], parentId: string) => {
for (const directory of Object.keys(tree)) {
if (tree[directory] instanceof File) {
if (!filesPerParentId[parentId]) filesPerParentId[parentId] = [];
filesPerParentId[parentId].push(tree[directory] as File);
} else {
const driveItem = await create(
{
company_id: context.companyId,
parent_id: parentId,
name: directory,
is_directory: true,
},
{},
);
if (driveItem?.id) {
await createDirectories(tree[directory] as FileTreeObject['tree'], driveItem.id);
} else {
throw new Error('Could not create directory');
}
}
}
};
await createDirectories(tree.tree, context.parentId);
// Upload files into directories
for (const parentId of Object.keys(filesPerParentId)) {
FileUploadService.upload(filesPerParentId[parentId], {
context: {
companyId: context.companyId,
parentId: parentId,
},
callback: (file, context) => {
console.log('created file: ', file);
if (file) {
create(
{
company_id: context.companyId,
workspace_id: 'drive', //We don't set workspace ID for now
parent_id: context.parentId,
name: file.metadata?.name,
size: file.upload_data?.size,
},
{
provider: 'internal',
application_id: '',
file_metadata: {
name: file.metadata?.name,
size: file.upload_data?.size,
mime: file.metadata?.mime,
thumbnails: file?.thumbnails,
source: 'internal',
external_id: file.id,
},
},
);
}
},
});
}
};
const uploadFromUrl = (
url: string,
name: string,
context: { companyId: string; parentId: string },
) => {
const request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'blob';
request.onload = function () {
try {
const file = new File([request.response], name);
FileUploadService.upload([file], {
context: {
companyId: context.companyId,
parentId: context.parentId,
},
callback: (file, context) => {
if (file) {
create(
{
company_id: context.companyId,
workspace_id: 'drive', //We don't set workspace ID for now
parent_id: context.parentId,
name: file.metadata?.name,
size: file.upload_data?.size,
},
{
provider: 'internal',
application_id: '',
file_metadata: {
name: file.metadata?.name,
size: file.upload_data?.size,
mime: file.metadata?.mime,
thumbnails: file?.thumbnails,
source: 'internal',
external_id: file.id,
},
},
);
}
},
});
} catch (e) {
ToasterService.error('Error while creating an empty file.');
}
};
request.send();
};
return { uploadTree, uploadFromUrl, uploadVersion };
};