@@ -0,0 +1,130 @@
|
||||
import Api from '../../global/framework/api-service';
|
||||
import { DriveItem, DriveItemDetails, DriveItemVersion } from '../types';
|
||||
import Workspace from 'app/deprecated/workspaces/workspaces';
|
||||
import Logger from 'features/global/framework/logger-service';
|
||||
export interface BaseSearchOptions {
|
||||
company_id?: string;
|
||||
workspace_id?: string;
|
||||
channel_id?: string;
|
||||
page_token?: string;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export type SearchDocumentsBody = {
|
||||
search?: string;
|
||||
company_id?: string;
|
||||
creator?: string;
|
||||
added?: string;
|
||||
};
|
||||
|
||||
let publicLinkToken: null | string = null;
|
||||
let tdriveTabToken: null | string = null;
|
||||
|
||||
export const setPublicLinkToken = (token: string | null) => {
|
||||
publicLinkToken = token;
|
||||
};
|
||||
|
||||
export const setTdriveTabToken = (token: string | null) => {
|
||||
tdriveTabToken = token;
|
||||
};
|
||||
|
||||
const appendPublicAndTdriveToken = (useAnd?: boolean) => {
|
||||
if (publicLinkToken) {
|
||||
return `${useAnd ? '&' : '?'}public_token=${publicLinkToken}`;
|
||||
}
|
||||
if (tdriveTabToken) {
|
||||
return `${useAnd ? '&' : '?'}tdrive_tab_token=${tdriveTabToken}`;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
export class DriveApiClient {
|
||||
private static logger = Logger.getLogger('MessageAPIClientService');
|
||||
static async get(companyId: string, id: string | 'trash' | '') {
|
||||
return await Api.get<DriveItemDetails>(
|
||||
`/internal/services/documents/v1/companies/${companyId}/item/${id}${appendPublicAndTdriveToken()}`,
|
||||
);
|
||||
}
|
||||
|
||||
static async remove(companyId: string, id: string | 'trash' | '') {
|
||||
return await Api.delete<void>(
|
||||
`/internal/services/documents/v1/companies/${companyId}/item/${id}${appendPublicAndTdriveToken()}`,
|
||||
);
|
||||
}
|
||||
|
||||
static async update(companyId: string, id: string, update: Partial<DriveItem>) {
|
||||
return await Api.post<Partial<DriveItem>, DriveItem>(
|
||||
`/internal/services/documents/v1/companies/${companyId}/item/${id}${appendPublicAndTdriveToken()}`,
|
||||
update,
|
||||
);
|
||||
}
|
||||
|
||||
static async create(
|
||||
companyId: string,
|
||||
data: { item: Partial<DriveItem>; version?: Partial<DriveItemVersion> },
|
||||
) {
|
||||
if (!data.version) data.version = {} as Partial<DriveItemVersion>;
|
||||
return await Api.post<
|
||||
{ item: Partial<DriveItem>; version: Partial<DriveItemVersion> },
|
||||
DriveItem
|
||||
>(
|
||||
`/internal/services/documents/v1/companies/${companyId}/item${appendPublicAndTdriveToken()}`,
|
||||
data as { item: Partial<DriveItem>; version: Partial<DriveItemVersion> },
|
||||
);
|
||||
}
|
||||
|
||||
static async createVersion(companyId: string, id: string, version: Partial<DriveItemVersion>) {
|
||||
return await Api.post<Partial<DriveItemVersion>, DriveItemVersion>(
|
||||
`/internal/services/documents/v1/companies/${companyId}/item/${id}/version${appendPublicAndTdriveToken()}`,
|
||||
version,
|
||||
);
|
||||
}
|
||||
|
||||
static async getDownloadToken(companyId: string, ids: string[], versionId?: string) {
|
||||
return Api.get<{ token: string }>(
|
||||
`/internal/services/documents/v1/companies/${companyId}/item/download/token` +
|
||||
`?items=${ids.join(',')}&version_id=${versionId}` +
|
||||
appendPublicAndTdriveToken(true),
|
||||
);
|
||||
}
|
||||
|
||||
static async getDownloadUrl(companyId: string, id: string, versionId?: string) {
|
||||
const { token } = await DriveApiClient.getDownloadToken(companyId, [id], versionId);
|
||||
if (versionId)
|
||||
return Api.route(
|
||||
`/internal/services/documents/v1/companies/${companyId}/item/${id}/download?version_id=${versionId}&token=${token}${appendPublicAndTdriveToken(
|
||||
true,
|
||||
)}`,
|
||||
);
|
||||
return Api.route(
|
||||
`/internal/services/documents/v1/companies/${companyId}/item/${id}/download?token=${token}${appendPublicAndTdriveToken(
|
||||
true,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
|
||||
static async getDownloadZipUrl(companyId: string, ids: string[]) {
|
||||
const { token } = await DriveApiClient.getDownloadToken(companyId, ids);
|
||||
return Api.route(
|
||||
`/internal/services/documents/v1/companies/${companyId}/item/download/zip` +
|
||||
`?items=${ids.join(',')}&token=${token}` +
|
||||
appendPublicAndTdriveToken(true),
|
||||
);
|
||||
}
|
||||
|
||||
static async search(searchString: string, options?: BaseSearchOptions) {
|
||||
const companyId = options?.company_id ? options.company_id : Workspace.currentGroupId;
|
||||
const query = `/internal/services/documents/v1/companies/${companyId}/search`;
|
||||
const searchData = {
|
||||
"search": searchString
|
||||
};
|
||||
const res = await Api.post<SearchDocumentsBody,{ entities: DriveItem[] }>(query, searchData);
|
||||
this.logger.debug(
|
||||
`Drive search by text "${searchString}". Found`,
|
||||
res.entities.length,
|
||||
'drive item(s)',
|
||||
);
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -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 };
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
import { atomFamily, atom } from 'recoil';
|
||||
import { DriveItem, DriveItemDetails } from '../types';
|
||||
|
||||
export const DriveItemChildrenAtom = atomFamily<DriveItem[], string>({
|
||||
key: 'DriveItemChildrenAtom',
|
||||
default: () => [],
|
||||
});
|
||||
|
||||
export const DriveItemAtom = atomFamily<Partial<DriveItemDetails> | null, string>({
|
||||
key: 'DriveItemAtom',
|
||||
default: () => null,
|
||||
});
|
||||
|
||||
export const DriveItemSelectedList = atom<{[key: string]: boolean }>({
|
||||
key: 'DriveItemSelectedList',
|
||||
default: {}
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
import { atom } from 'recoil';
|
||||
import { DriveItem, DriveItemDetails } from '../types';
|
||||
|
||||
export const DriveViewerState = atom<{
|
||||
item: null | DriveItem;
|
||||
details?: DriveItemDetails;
|
||||
loading: boolean;
|
||||
}>({
|
||||
key: "DriveViewerState",
|
||||
default: {
|
||||
item: null,
|
||||
loading: true
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
export type DriveItemDetails = {
|
||||
item: DriveItem;
|
||||
versions: DriveItemVersion[];
|
||||
children: DriveItem[];
|
||||
path: DriveItem[];
|
||||
access: 'read' | 'write' | 'manage';
|
||||
websockets: {
|
||||
room: string;
|
||||
token?: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
export type DriveItem = {
|
||||
id: string;
|
||||
company_id: string;
|
||||
workspace_id: string;
|
||||
|
||||
parent_id: string;
|
||||
in_trash: boolean;
|
||||
is_directory: boolean;
|
||||
name: string;
|
||||
extension: string;
|
||||
description: string;
|
||||
tags: [];
|
||||
|
||||
added: string;
|
||||
last_modified: string;
|
||||
last_version_cache: DriveItemVersion;
|
||||
|
||||
access_info: DriveItemAccessInfo;
|
||||
|
||||
size: number;
|
||||
};
|
||||
|
||||
export type DriveFileAccessLevel = 'none' | 'read' | 'write' | 'manage';
|
||||
|
||||
export type DriveItemAccessInfo = {
|
||||
public?: {
|
||||
token: string;
|
||||
level: DriveFileAccessLevel;
|
||||
};
|
||||
entities: AuthEntity[];
|
||||
};
|
||||
|
||||
type AuthEntity = {
|
||||
type: 'user' | 'channel' | 'company' | 'folder';
|
||||
id: string | 'parent';
|
||||
level: DriveFileAccessLevel;
|
||||
};
|
||||
|
||||
export type DriveItemVersion = {
|
||||
//Id of the version of the file
|
||||
id: string;
|
||||
|
||||
//The file itself, using the existing new node "file" entity
|
||||
provider: string | 'drive' | 'internal'; //Equivalent to "source" in tdrive/backend/node/src/services/messages/entities/message-files.ts
|
||||
drive_item_id: string;
|
||||
file_metadata: FileMetadata; //New field
|
||||
|
||||
date_added: number;
|
||||
creator_id: string;
|
||||
application_id: string;
|
||||
};
|
||||
|
||||
export type FileMetadata = {
|
||||
source: 'internal' | 'drive' | string; //Uuid of the corresponding connector
|
||||
external_id: string;
|
||||
|
||||
name?: string; //Original file name
|
||||
mime?: string; //Original file mime
|
||||
size?: number; //Original file weight
|
||||
thumbnails?: Thumbnail[]; //Url to thumbnail (or set it to undefined if no relevant)
|
||||
};
|
||||
|
||||
export type Thumbnail = {
|
||||
index?: number;
|
||||
id?: string;
|
||||
|
||||
type?: string;
|
||||
size?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
|
||||
url: string;
|
||||
full_url?: string;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
export const formatBytes = (bytes: number, decimals = 2) => {
|
||||
if (!+bytes) return '0 KB';
|
||||
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
||||
};
|
||||
Reference in New Issue
Block a user