Skip to content

Commit

Permalink
Merge pull request #1282 from internxt/bugfix/PB-2375-check-duplicate…
Browse files Browse the repository at this point in the history
…-items-before-uploading

[PB-2375] bugfix/Check duplicated items before uploading on Drive web
  • Loading branch information
CandelR authored Oct 2, 2024
2 parents aa68226 + f4e0f38 commit fc79cca
Show file tree
Hide file tree
Showing 30 changed files with 985 additions and 304 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"@iconscout/react-unicons": "^1.1.6",
"@internxt/inxt-js": "=1.2.21",
"@internxt/lib": "^1.2.0",
"@internxt/sdk": "^1.5.15",
"@internxt/sdk": "^1.5.16",
"@phosphor-icons/react": "^2.1.7",
"@popperjs/core": "^2.11.6",
"@reduxjs/toolkit": "^1.6.0",
Expand Down
16 changes: 12 additions & 4 deletions src/app/drive/components/DriveExplorer/DriveExplorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -370,11 +370,15 @@ const DriveExplorer = (props: DriveExplorerProps): JSX.Element => {
folderInputRef.current?.click();
}, [currentFolderId]);

const onUploadFileInputChanged = (e) => {
const onUploadFileInputChanged = async (e) => {
const files = e.target.files;

if (files.length <= UPLOAD_ITEMS_LIMIT) {
const unrepeatedUploadedFiles = handleRepeatedUploadingFiles(Array.from(files), items, dispatch) as File[];
const unrepeatedUploadedFiles = (await handleRepeatedUploadingFiles(
Array.from(files),
dispatch,
currentFolderId,
)) as File[];
dispatch(
storageThunks.uploadItemsThunk({
files: Array.from(unrepeatedUploadedFiles),
Expand Down Expand Up @@ -920,7 +924,7 @@ const uploadItems = async (props: DriveExplorerProps, rootList: IRoot[], files:
itemsDragged: items,
},
});
const unrepeatedUploadedFiles = handleRepeatedUploadingFiles(files, items, dispatch) as File[];
const unrepeatedUploadedFiles = (await handleRepeatedUploadingFiles(files, dispatch, currentFolderId)) as File[];
// files where dragged directly
await dispatch(
storageThunks.uploadItemsThunk({
Expand All @@ -945,7 +949,11 @@ const uploadItems = async (props: DriveExplorerProps, rootList: IRoot[], files:
itemsDragged: items,
},
});
const unrepeatedUploadedFolders = handleRepeatedUploadingFolders(rootList, items, dispatch) as IRoot[];
const unrepeatedUploadedFolders = (await handleRepeatedUploadingFolders(
rootList,
dispatch,
currentFolderId,
)) as IRoot[];

if (unrepeatedUploadedFolders.length > 0) {
const folderDataToUpload = unrepeatedUploadedFolders.map((root) => ({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ConnectDragSource, ConnectDropTarget, useDrag, useDrop } from 'react-dnd';
import { NativeTypes } from 'react-dnd-html5-backend';
import { SdkFactory } from '../../../../../core/factory/sdk';
import { transformDraggedItems } from '../../../../../core/services/drag-and-drop.service';
import { DragAndDropType } from '../../../../../core/types';
import { useAppDispatch, useAppSelector } from '../../../../../store/hooks';
Expand Down Expand Up @@ -49,7 +48,6 @@ export const useDriveItemDrop = (item: DriveItemData): DriveItemDrop => {
const dispatch = useAppDispatch();
const isSomeItemSelected = useAppSelector(storageSelectors.isSomeItemSelected);
const { selectedItems } = useAppSelector((state) => state.storage);
const workspacesCredentials = useAppSelector((state) => state.workspaces.workspaceCredentials);
const namePath = useAppSelector((state) => state.storage.namePath);
const [{ isDraggingOverThisItem, canDrop }, connectDropTarget] = useDrop<
DriveItemData | DriveItemData[],
Expand Down Expand Up @@ -88,30 +86,10 @@ export const useDriveItemDrop = (item: DriveItemData): DriveItemDrop => {
return i.isFolder;
});

const storageClient = SdkFactory.getNewApiInstance().createNewStorageClient();

dispatch(storageActions.setMoveDestinationFolderId(item.uuid));

const [folderContentPromise] = storageClient.getFolderContentByUuid(
item.uuid,
false,
workspacesCredentials?.tokenHeader,
);
const { children: foldersInDestinationFolder, files: filesInDestinationFolder } = await folderContentPromise;
const foldersInDestinationFolderParsed = foldersInDestinationFolder.map((folder) => ({
...folder,
isFolder: true,
}));
const unrepeatedFiles = handleRepeatedUploadingFiles(
filesToMove,
filesInDestinationFolder as DriveItemData[],
dispatch,
);
const unrepeatedFolders = handleRepeatedUploadingFolders(
foldersToMove,
foldersInDestinationFolderParsed as DriveItemData[],
dispatch,
);
const unrepeatedFiles = await handleRepeatedUploadingFiles(filesToMove, dispatch, item.uuid);
const unrepeatedFolders = await handleRepeatedUploadingFolders(foldersToMove, dispatch, item.uuid);
const unrepeatedItems: DriveItemData[] = [...unrepeatedFiles, ...unrepeatedFolders] as DriveItemData[];

if (unrepeatedItems.length === itemsToMove.length)
Expand Down
32 changes: 24 additions & 8 deletions src/app/drive/components/MoveItemsDialog/MoveItemsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ import { uiActions } from 'app/store/slices/ui';
import folderImage from 'assets/icons/light/folder.svg';
import { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { DriveItemData, FolderPathDialog } from '../../types';
import {
handleRepeatedUploadingFiles,
handleRepeatedUploadingFolders,
} from '../../../store/slices/storage/storage.thunks/renameItemsThunk';
import { IRoot } from '../../../store/slices/storage/types';
import { DriveFileData, DriveFolderData, DriveItemData, FolderPathDialog } from '../../types';
import CreateFolderDialog from '../CreateFolderDialog/CreateFolderDialog';

interface MoveItemsDialogProps {
Expand Down Expand Up @@ -152,6 +157,8 @@ const MoveItemsDialog = (props: MoveItemsDialogProps): JSX.Element => {

const onAccept = async (destinationFolderId, name, namePaths): Promise<void> => {
try {
dispatch(storageActions.setMoveDestinationFolderId(destinationFolderId));

setIsLoading(true);
if (itemsToMove.length > 0) {
if (destinationFolderId != currentFolderId) {
Expand All @@ -162,14 +169,23 @@ const MoveItemsDialog = (props: MoveItemsDialogProps): JSX.Element => {
destinationFolderId = currentFolderId;
}

await dispatch(
storageThunks.moveItemsThunk({
items: itemsToMove,
destinationFolderId: destinationFolderId,
}),
);
}
const files = itemsToMove.filter((item) => item.type !== 'folder') as DriveFileData[];
const folders = itemsToMove.filter((item) => item.type === 'folder') as (IRoot | DriveFolderData)[];

const filesWithoutDuplicates = await handleRepeatedUploadingFiles(files, dispatch, destinationFolderId);
const foldersWithoutDuplicates = await handleRepeatedUploadingFolders(folders, dispatch, destinationFolderId);

const itemsToMoveWithoutDuplicates = [...filesWithoutDuplicates, ...foldersWithoutDuplicates];

if (itemsToMoveWithoutDuplicates.length > 0) {
await dispatch(
storageThunks.moveItemsThunk({
items: itemsToMoveWithoutDuplicates as DriveItemData[],
destinationFolderId: destinationFolderId,
}),
);
}
}
props.onItemsMoved?.();

setIsLoading(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,17 @@ const NameCollisionContainer: FC<NameCollisionContainerProps> = ({
};

const keepAndMoveItem = async (itemsToUpload: DriveItemData[]) => {
await dispatch(storageThunks.renameItemsThunk({ items: itemsToUpload, destinationFolderId: folderId }));
dispatch(
storageThunks.moveItemsThunk({
await dispatch(
storageThunks.renameItemsThunk({
items: itemsToUpload,
destinationFolderId: moveDestinationFolderId as string,
destinationFolderId: folderId,
onRenameSuccess: (itemToUpload: DriveItemData) =>
dispatch(
storageThunks.moveItemsThunk({
items: [itemToUpload],
destinationFolderId: moveDestinationFolderId as string,
}),
),
}),
);
};
Expand Down
7 changes: 7 additions & 0 deletions src/app/drive/services/file.service/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface FileToUpload {
name: string;
size: number;
type: string;
content: File;
parentFolderId: string;
}
1 change: 1 addition & 0 deletions src/app/drive/services/file.service/uploadFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import notificationsService, { ToastType } from '../../../notifications/services
import { getEnvironmentConfig } from '../network.service';
import { generateThumbnailFromFile } from '../thumbnail.service';

// TODO: REMOVE FROM HERE, DUPLICATED TO MAKE TESTS
export interface FileToUpload {
name: string;
size: number;
Expand Down
28 changes: 27 additions & 1 deletion src/app/drive/services/new-storage.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { DriveFileData, FolderAncestor, FolderMeta, FolderTreeResponse } from '@internxt/sdk/dist/drive/storage/types';
import {
CheckDuplicatedFilesResponse,
CheckDuplicatedFoldersResponse,
DriveFileData,
FileStructure,
FolderAncestor,
FolderMeta,
FolderTreeResponse,
} from '@internxt/sdk/dist/drive/storage/types';
import { SdkFactory } from '../../core/factory/sdk';

export async function searchItemsByName(name: string): Promise<DriveFileData[]> {
Expand All @@ -23,11 +31,29 @@ export async function getFolderTree(uuid: string): Promise<FolderTreeResponse> {
return storageClient.getFolderTree(uuid);
}

export async function checkDuplicatedFiles(
folderUuid: string,
filesList: FileStructure[],
): Promise<CheckDuplicatedFilesResponse> {
const storageClient = SdkFactory.getNewApiInstance().createNewStorageClient();
return storageClient.checkDuplicatedFiles({ folderUuid, filesList });
}

export async function checkDuplicatedFolders(
folderUuid: string,
folderNamesList: string[],
): Promise<CheckDuplicatedFoldersResponse> {
const storageClient = SdkFactory.getNewApiInstance().createNewStorageClient();
return storageClient.checkDuplicatedFolders({ folderUuid, folderNamesList });
}

const newStorageService = {
searchItemsByName,
getFolderAncestors,
getFolderMeta,
getFolderTree,
checkDuplicatedFiles,
checkDuplicatedFolders,
};

export default newStorageService;
1 change: 1 addition & 0 deletions src/app/drive/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface DriveFolderData {
shares?: Array<ShareLink>;
sharings?: { type: string; id: string }[];
uuid: string;
type?: string;
}

export interface DriveFolderMetadataPayload {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import storageSelectors from 'app/store/slices/storage/storage.selectors';
import storageThunks from 'app/store/slices/storage/storage.thunks';
import { DropTargetMonitor, useDrop } from 'react-dnd';
import { NativeTypes } from 'react-dnd-html5-backend';
import { SdkFactory } from '../../../../core/factory/sdk';
import { storageActions } from '../../../../store/slices/storage';
import {
handleRepeatedUploadingFiles,
Expand All @@ -26,7 +25,6 @@ interface BreadcrumbsItemProps {
const BreadcrumbsItem = (props: BreadcrumbsItemProps): JSX.Element => {
const dispatch = useAppDispatch();
const namePath = useAppSelector((state) => state.storage.namePath);
const workspacesCredentials = useAppSelector((state) => state.workspaces.workspaceCredentials);
const isSomeItemSelected = useAppSelector(storageSelectors.isSomeItemSelected);
const selectedItems = useAppSelector((state) => state.storage.selectedItems);

Expand All @@ -52,31 +50,10 @@ const BreadcrumbsItem = (props: BreadcrumbsItemProps): JSX.Element => {
});

dispatch(storageActions.setMoveDestinationFolderId(props.item.uuid));
const storageClient = SdkFactory.getNewApiInstance().createNewStorageClient();

const [folderContentPromise] = storageClient.getFolderContentByUuid(
props.item.uuid,
false,
workspacesCredentials?.tokenHeader,
);

const { children: foldersInDestinationFolder, files: filesInDestinationFolder } = await folderContentPromise;

const foldersInDestinationFolderParsed = foldersInDestinationFolder.map((folder) => ({
...folder,
isFolder: true,
}));

const unrepeatedFiles = handleRepeatedUploadingFiles(
filesToMove,
filesInDestinationFolder as DriveItemData[],
dispatch,
);
const unrepeatedFolders = handleRepeatedUploadingFolders(
foldersToMove,
foldersInDestinationFolderParsed as DriveItemData[],
dispatch,
);
const folderUuid = props.item.uuid;
const unrepeatedFiles = await handleRepeatedUploadingFiles(filesToMove, dispatch, folderUuid);
const unrepeatedFolders = await handleRepeatedUploadingFolders(foldersToMove, dispatch, folderUuid);
const unrepeatedItems: DriveItemData[] = [...unrepeatedFiles, ...unrepeatedFolders] as DriveItemData[];

if (unrepeatedItems.length === itemsToMove.length) dispatch(storageActions.setMoveDestinationFolderId(null));
Expand Down
65 changes: 65 additions & 0 deletions src/app/store/slices/storage/fileUtils/checkDuplicatedFiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { items as itemUtils } from '@internxt/lib';
import { DriveFileData } from '@internxt/sdk/dist/drive/storage/types';
import newStorageService from '../../../../drive/services/new-storage.service';

export interface DuplicatedFilesResult {
duplicatedFilesResponse: DriveFileData[];
filesWithDuplicates: (File | DriveFileData)[];
filesWithoutDuplicates: (File | DriveFileData)[];
}

export const checkDuplicatedFiles = async (
files: (File | DriveFileData)[],
parentFolderId: string,
): Promise<DuplicatedFilesResult> => {
if (files.length === 0) {
return {
duplicatedFilesResponse: [],
filesWithDuplicates: [],
filesWithoutDuplicates: files,
} as DuplicatedFilesResult;
}

const parsedFiles = files.map(parseFile);
const checkDuplicatedFileResponse = await newStorageService.checkDuplicatedFiles(parentFolderId, parsedFiles);

const duplicatedFilesResponse = checkDuplicatedFileResponse.existentFiles;

const { filesWithDuplicates, filesWithoutDuplicates } = parsedFiles.reduce(
(acc, parsedFile) => {
const isDuplicated = duplicatedFilesResponse.some(
(duplicatedFile) =>
duplicatedFile.plainName === parsedFile.plainName && duplicatedFile.type === parsedFile.type,
);

if (isDuplicated) {
acc.filesWithDuplicates.push(parsedFile.originalFile);
} else {
acc.filesWithoutDuplicates.push(parsedFile.originalFile);
}

return acc;
},
{ filesWithDuplicates: [], filesWithoutDuplicates: [] } as {
filesWithDuplicates: (File | DriveFileData)[];
filesWithoutDuplicates: (File | DriveFileData)[];
},
);

return { duplicatedFilesResponse, filesWithoutDuplicates, filesWithDuplicates };
};

interface ParsedFile {
plainName: string;
type: string;
originalFile: File | DriveFileData;
}

const parseFile = (file: File | DriveFileData): ParsedFile => {
if (file instanceof File) {
const { filename, extension } = itemUtils.getFilenameAndExt(file.name);
return { plainName: filename, type: extension, originalFile: file };
} else {
return { plainName: file.name, type: file.type, originalFile: file };
}
};
Loading

0 comments on commit fc79cca

Please sign in to comment.