Web: Files: added queue for secondary operations

This commit is contained in:
Nikita Gopienko 2023-03-29 16:18:30 +03:00
parent 51d6059d63
commit d9fab2d287
3 changed files with 175 additions and 80 deletions

View File

@ -28,15 +28,13 @@ import {
FileStatus,
FolderType,
} from "@docspace/common/constants";
import { makeAutoObservable, runInAction } from "mobx";
import { makeAutoObservable } from "mobx";
import { isMobile } from "react-device-detect";
import toastr from "@docspace/components/toast/toastr";
import { TIMEOUT } from "@docspace/client/src/helpers/filesConstants";
import { checkProtocol } from "../helpers/files-helpers";
import { combineUrl } from "@docspace/common/utils";
import config from "PACKAGE_FILE";
import FilesFilter from "@docspace/common/api/files/filter";
import api from "@docspace/common/api";
import { isTablet } from "@docspace/components/utils/device";
import { getCategoryType } from "SRC_DIR/helpers/utils";
import { muteRoomNotification } from "@docspace/common/api/settings";
@ -44,6 +42,7 @@ import { CategoryType } from "SRC_DIR/helpers/constants";
import RoomsFilter from "@docspace/common/api/rooms/filter";
import { RoomSearchArea } from "@docspace/common/constants";
import { getObjectByLocation } from "@docspace/common/utils";
import uniqueid from "lodash/uniqueId";
class FilesActionStore {
authStore;
@ -94,7 +93,7 @@ class FilesActionStore {
}
};
updateCurrentFolder = (fileIds, folderIds, clearSelection) => {
updateCurrentFolder = (fileIds, folderIds, clearSelection, operationId) => {
const {
clearSecondaryProgressData,
} = this.uploadDataStore.secondaryProgressDataStore;
@ -140,7 +139,10 @@ class FilesActionStore {
newFilter ? newFilter : roomsFilter.clone()
).finally(() => {
this.dialogsStore.setIsFolderActions(false);
return setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
return setTimeout(
() => clearSecondaryProgressData(operationId),
TIMEOUT
);
});
} else {
fetchFiles(
@ -151,7 +153,10 @@ class FilesActionStore {
clearSelection
).finally(() => {
this.dialogsStore.setIsFolderActions(false);
return setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
return setTimeout(
() => clearSecondaryProgressData(operationId),
TIMEOUT
);
});
}
};
@ -208,6 +213,8 @@ class FilesActionStore {
clearSecondaryProgressData,
} = secondaryProgressDataStore;
const operationId = uniqueid("operation_");
const toFolderId = folderId ? folderId : this.selectedFolderStore.id;
setSecondaryProgressBarData({
@ -216,17 +223,18 @@ class FilesActionStore {
percent: 0,
label: "",
alert: false,
operationId,
});
const tree = this.convertToTree(emptyFolders);
await this.createFolderTree(tree, toFolderId);
this.updateCurrentFolder(null, [folderId]);
this.updateCurrentFolder(null, [folderId], null, operationId);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT);
};
updateFilesAfterDelete = () => {
updateFilesAfterDelete = (operationId) => {
const { setSelected } = this.filesStore;
const {
clearSecondaryProgressData,
@ -235,7 +243,7 @@ class FilesActionStore {
setSelected("close");
this.dialogsStore.setIsFolderActions(false);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT);
};
deleteAction = async (
@ -260,6 +268,8 @@ class FilesActionStore {
const currentFolderId = this.selectedFolderStore.id;
const operationId = uniqueid("operation_");
setSecondaryProgressBarData({
icon: "trash",
visible: true,
@ -267,6 +277,7 @@ class FilesActionStore {
label: translations.deleteOperation,
alert: false,
filesCount: selection.length,
operationId,
});
const deleteAfter = false; //Delete after finished TODO: get from settings
@ -308,6 +319,7 @@ class FilesActionStore {
const pbData = {
icon: "trash",
label: translations.deleteOperation,
operationId,
};
await this.uploadDataStore.loopFilesOperations(data, pbData);
@ -326,10 +338,10 @@ class FilesActionStore {
};
if (withPaging || this.dialogsStore.isFolderActions) {
this.updateCurrentFolder(fileIds, folderIds, false);
this.updateCurrentFolder(fileIds, folderIds, false, operationId);
showToast();
} else {
this.updateFilesAfterDelete(folderIds);
this.updateFilesAfterDelete(operationId);
this.filesStore.removeFiles(fileIds, folderIds, showToast);
this.uploadDataStore.removeFiles(fileIds);
}
@ -352,8 +364,9 @@ class FilesActionStore {
setSecondaryProgressBarData({
visible: true,
alert: true,
operationId,
});
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT);
return toastr.error(err.message ? err.message : err);
} finally {
this.filesStore.setOperationAction(false);
@ -379,12 +392,15 @@ class FilesActionStore {
const folderIds = folders.map((f) => f.id);
if (isRecycleBinFolder) addActiveItems(fileIds, folderIds);
const operationId = uniqueid("operation_");
setSecondaryProgressBarData({
icon: "trash",
visible: true,
percent: 0,
label: translations.deleteOperation,
alert: false,
operationId,
});
try {
@ -394,10 +410,11 @@ class FilesActionStore {
const pbData = {
icon: "trash",
label: translations.deleteOperation,
operationId,
};
await loopFilesOperations(data, pbData);
toastr.success(translations.successOperation);
this.updateCurrentFolder(fileIds, folderIds);
this.updateCurrentFolder(fileIds, folderIds, null, operationId);
getIsEmptyTrash();
clearActiveOperations(fileIds, folderIds);
});
@ -406,8 +423,9 @@ class FilesActionStore {
setSecondaryProgressBarData({
visible: true,
alert: true,
operationId,
});
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT);
return toastr.error(err.message ? err.message : err);
}
};
@ -434,8 +452,11 @@ class FilesActionStore {
percent: 0,
label: translations.deleteOperation,
alert: false,
operationId,
});
const operationId = uniqueid("operation_");
try {
await removeFiles(folderIds, [], true, true).then(async (res) => {
if (res[0]?.error) return Promise.reject(res[0].error);
@ -443,10 +464,11 @@ class FilesActionStore {
const pbData = {
icon: "trash",
label: translations.deleteOperation,
operationId,
};
await loopFilesOperations(data, pbData);
toastr.success(translations.successOperation);
this.updateCurrentFolder(null, folderIds);
this.updateCurrentFolder(null, folderIds, null, operationId);
// getIsEmptyTrash();
clearActiveOperations(null, folderIds);
});
@ -455,8 +477,9 @@ class FilesActionStore {
setSecondaryProgressBarData({
visible: true,
alert: true,
operationId,
});
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT);
return toastr.error(err.message ? err.message : err);
}
@ -482,12 +505,15 @@ class FilesActionStore {
this.setIsBulkDownload(true);
const operationId = uniqueid("operation_");
setSecondaryProgressBarData({
icon: "file",
visible: true,
percent: 0,
label,
alert: false,
operationId,
});
const fileIds = fileConvertIds.map((f) => f.key || f);
@ -499,6 +525,7 @@ class FilesActionStore {
const pbData = {
icon: "file",
label,
operationId,
};
const item =
@ -519,10 +546,11 @@ class FilesActionStore {
setSecondaryProgressBarData({
visible: true,
alert: true,
operationId,
});
}
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT);
!item.url && toastr.error(translations.error, null, 0, true);
});
} catch (err) {
@ -531,8 +559,9 @@ class FilesActionStore {
setSecondaryProgressBarData({
visible: true,
alert: true,
operationId,
});
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT);
return toastr.error(err.message ? err.message : err);
}
};
@ -674,12 +703,15 @@ class FilesActionStore {
this.dialogsStore.setIsRoomDelete(isRoom);
this.dialogsStore.setDeleteDialogVisible(true);
} else {
const operationId = uniqueid("operation_");
setSecondaryProgressBarData({
icon: "trash",
visible: true,
percent: 0,
label: translations?.deleteOperation,
alert: false,
operationId,
});
try {
@ -693,8 +725,9 @@ class FilesActionStore {
setSecondaryProgressBarData({
visible: true,
alert: true,
operationId,
});
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT);
return toastr.error(err.message ? err.message : err);
}
}
@ -707,6 +740,7 @@ class FilesActionStore {
const pbData = {
icon: "trash",
label: translations?.deleteOperation,
operationId,
};
this.filesStore.setOperationAction(true);
@ -721,10 +755,10 @@ class FilesActionStore {
await this.uploadDataStore.loopFilesOperations(data, pbData);
if (withPaging) {
this.updateCurrentFolder([itemId]);
this.updateCurrentFolder([itemId], null, null, operationId);
toastr.success(translations.successRemoveFile);
} else {
this.updateFilesAfterDelete();
this.updateFilesAfterDelete(operationId);
this.filesStore.removeFiles([itemId], null, () =>
toastr.success(translations.successRemoveFile)
);
@ -741,7 +775,7 @@ class FilesActionStore {
if (res[0]?.error) return Promise.reject(res[0].error);
const data = res ? res : null;
await this.uploadDataStore.loopFilesOperations(data, pbData);
this.updateCurrentFolder(null, [itemId]);
this.updateCurrentFolder(null, [itemId], null, operationId);
})
.then(() =>
toastr.success(
@ -762,10 +796,10 @@ class FilesActionStore {
await this.uploadDataStore.loopFilesOperations(data, pbData);
if (withPaging) {
this.updateCurrentFolder(null, [itemId]);
this.updateCurrentFolder(null, [itemId], null, operationId);
toastr.success(translations.successRemoveFolder);
} else {
this.updateFilesAfterDelete([itemId]);
this.updateFilesAfterDelete(operationId);
this.filesStore.removeFiles(null, [itemId], () =>
toastr.success(translations.successRemoveFolder)
);
@ -837,6 +871,7 @@ class FilesActionStore {
item.fileExst ? fileIds.push(item.id) : folderIds.push(item.id);
const conflictResolveType = ConflictResolveType.Duplicate;
const deleteAfter = false; //TODO: get from settings
const operationId = uniqueid("operation_");
setSecondaryProgressBarData({
icon: "duplicate",
@ -845,6 +880,7 @@ class FilesActionStore {
label,
alert: false,
filesCount: filesCount + fileIds.length,
operationId,
});
this.filesStore.addActiveItems(fileIds, folderIds);
@ -854,7 +890,8 @@ class FilesActionStore {
folderIds,
fileIds,
conflictResolveType,
deleteAfter
deleteAfter,
operationId
);
};
@ -917,7 +954,7 @@ class FilesActionStore {
return Promise.all(actions)
.then(() => {
this.updateCurrentFolder(null, items);
this.updateCurrentFolder(null, items, null, operationId);
if (selection) {
setSelection({ ...selection, pinned: true });
}
@ -931,7 +968,7 @@ class FilesActionStore {
});
return Promise.all(actions)
.then(() => {
this.updateCurrentFolder(null, items);
this.updateCurrentFolder(null, items, null, operationId);
if (selection) {
setSelection({ ...selection, pinned: false });
}
@ -972,7 +1009,7 @@ class FilesActionStore {
.catch((e) => toastr.error(e))
.finally(() => {
Promise.all([
this.updateCurrentFolder(null, [id]),
this.updateCurrentFolder(null, [id], null, operationId),
this.treeFoldersStore.fetchTreeFolders(),
]);
});
@ -1005,6 +1042,8 @@ class FilesActionStore {
return;
}
const operationId = uniqueid("operation_");
const items = Array.isArray(folders)
? folders.map((x) => (x?.id ? x.id : x))
: [folders.id];
@ -1015,6 +1054,7 @@ class FilesActionStore {
percent: 0,
label: "Archive room",
alert: false,
operationId,
});
addActiveItems(null, items);
@ -1030,6 +1070,7 @@ class FilesActionStore {
const pbData = {
label: "Archive rooms operation",
operationId,
};
const data = lastResult || null;
@ -1054,7 +1095,7 @@ class FilesActionStore {
setSelectedFolder(roomsFolder);
}
this.updateCurrentFolder();
this.updateCurrentFolder(null, null, null, operationId);
})
.then(() => setPortalQuota())
.then(() => {
@ -1073,8 +1114,9 @@ class FilesActionStore {
setSecondaryProgressBarData({
visible: true,
alert: true,
operationId,
});
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT);
return toastr.error(err.message ? err.message : err);
})
.finally(() => {
@ -1091,6 +1133,7 @@ class FilesActionStore {
const pbData = {
label: "Restore rooms from archive operation",
operationId,
};
const data = lastResult || null;
@ -1098,7 +1141,7 @@ class FilesActionStore {
await this.uploadDataStore.loopFilesOperations(data, pbData);
this.updateCurrentFolder(null, [items]);
this.updateCurrentFolder(null, [items], null, operationId);
})
.then(() => setPortalQuota())
.then(() => {
@ -1117,8 +1160,9 @@ class FilesActionStore {
setSecondaryProgressBarData({
visible: true,
alert: true,
operationId,
});
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT);
return toastr.error(err.message ? err.message : err);
})
.finally(() => {
@ -1260,17 +1304,20 @@ class FilesActionStore {
clearSecondaryProgressData,
} = this.uploadDataStore.secondaryProgressDataStore;
const operationId = uniqueid("operation_");
setSecondaryProgressBarData({
icon: "file",
label: "", //TODO: add translation if need "MarkAsRead": "Mark all as read",
percent: 0,
visible: true,
operationId,
});
return markAsRead(folderIds, fileIds)
.then(async (res) => {
const data = res[0] ? res[0] : null;
const pbData = { icon: "file" };
const pbData = { icon: "file", operationId };
await this.uploadDataStore.loopFilesOperations(data, pbData);
})
.then(() => {
@ -1284,7 +1331,9 @@ class FilesActionStore {
updateFileStatus(index, item.fileStatus & ~FileStatus.IsNew);
})
.catch((err) => toastr.error(err))
.finally(() => setTimeout(() => clearSecondaryProgressData(), TIMEOUT));
.finally(() =>
setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT)
);
};
moveDragItems = (destFolderId, folderTitle, translations) => {
@ -1515,12 +1564,15 @@ class FilesActionStore {
clearSecondaryProgressData,
} = secondaryProgressDataStore;
const operationId = uniqueid("operation_");
setSecondaryProgressBarData({
icon: "trash",
visible: true,
percent: 0,
label: translations?.deleteOperation,
alert: false,
operationId,
});
try {
@ -1536,8 +1588,9 @@ class FilesActionStore {
setSecondaryProgressBarData({
visible: true,
alert: true,
operationId,
});
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT);
return toastr.error(err.message ? err.message : err);
} finally {
this.setGroupMenuBlocked(false);

View File

@ -1,4 +1,4 @@
import { makeObservable, action, observable, computed } from "mobx";
import { makeAutoObservable } from "mobx";
class SecondaryProgressDataStore {
percent = 0;
@ -10,32 +10,30 @@ class SecondaryProgressDataStore {
itemsSelectionLength = 0;
itemsSelectionTitle = null;
secondaryOperationsArray = [];
constructor() {
makeObservable(this, {
percent: observable,
label: observable,
visible: observable,
icon: observable,
alert: observable,
filesCount: observable,
itemsSelectionLength: observable,
itemsSelectionTitle: observable,
isSecondaryProgressFinished: computed,
setSecondaryProgressBarData: action,
clearSecondaryProgressData: action,
setItemsSelectionLength: action,
setItemsSelectionTitle: action,
});
makeAutoObservable(this);
}
setSecondaryProgressBarData = (secondaryProgressData) => {
const progressDataItems = Object.keys(secondaryProgressData);
for (let key of progressDataItems) {
if (key in this) {
this[key] = secondaryProgressData[key];
const progressIndex = this.secondaryOperationsArray.findIndex(
(p) => p.operationId === secondaryProgressData.operationId
);
if (progressIndex !== -1) {
this.secondaryOperationsArray[progressIndex] = secondaryProgressData;
if (progressIndex === 0) {
const progressDataItems = Object.keys(secondaryProgressData);
for (let key of progressDataItems) {
if (key in this) {
this[key] = secondaryProgressData[key];
}
}
}
} else {
this.secondaryOperationsArray.push(secondaryProgressData);
}
};
@ -47,13 +45,38 @@ class SecondaryProgressDataStore {
this.itemsSelectionLength = itemsSelectionLength;
};
clearSecondaryProgressData = () => {
this.percent = 0;
this.label = "";
this.visible = false;
this.icon = "";
this.alert = false;
this.filesCount = 0;
clearSecondaryProgressData = (operationId) => {
const progressIndex = this.secondaryOperationsArray.findIndex(
(p) => p.operationId === operationId
);
if (progressIndex !== -1) {
this.secondaryOperationsArray = this.secondaryOperationsArray.filter(
(p) => p.operationId !== operationId
);
if (this.secondaryOperationsArray.length > 0) {
const nextOperation = this.secondaryOperationsArray[0];
this.percent = nextOperation.percent;
this.label = nextOperation.label;
this.visible = nextOperation.visible;
this.icon = nextOperation.icon;
this.alert = nextOperation.alert;
this.filesCount = nextOperation.filesCount;
return;
}
}
if (this.secondaryOperationsArray.length <= 1) {
this.percent = 0;
this.label = "";
this.visible = false;
this.icon = "";
this.alert = false;
this.filesCount = 0;
}
};
get isSecondaryProgressFinished() {

View File

@ -1310,7 +1310,8 @@ class UploadDataStore {
folderIds,
fileIds,
conflictResolveType,
deleteAfter
deleteAfter,
operationId
) => {
const {
setSecondaryProgressBarData,
@ -1325,10 +1326,12 @@ class UploadDataStore {
deleteAfter
)
.then((res) => {
if (res[0]?.error) return Promise.reject(res[0].error);
if (res[res.length - 1]?.error) {
return Promise.reject(res[res.length - 1]);
}
const data = res[0] ? res[0] : null;
const pbData = { icon: "duplicate" };
const data = res[res.length - 1] ? res[res.length - 1] : null;
const pbData = { icon: "duplicate", operationId };
return this.loopFilesOperations(data, pbData)
.then(() =>
@ -1344,9 +1347,10 @@ class UploadDataStore {
setSecondaryProgressBarData({
visible: true,
alert: true,
operationId,
});
this.clearActiveOperations(fileIds, folderIds);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT);
return Promise.reject(err);
});
};
@ -1356,7 +1360,8 @@ class UploadDataStore {
folderIds,
fileIds,
conflictResolveType,
deleteAfter
deleteAfter,
operationId
) => {
const {
setSecondaryProgressBarData,
@ -1371,8 +1376,8 @@ class UploadDataStore {
deleteAfter
)
.then((res) => {
const data = res[0] ? res[0] : null;
const pbData = { icon: "move" };
const data = res[res.length - 1] ? res[res.length - 1] : null;
const pbData = { icon: "move", operationId };
return this.loopFilesOperations(data, pbData)
.then(() =>
@ -1388,9 +1393,10 @@ class UploadDataStore {
setSecondaryProgressBarData({
visible: true,
alert: true,
operationId,
});
this.clearActiveOperations(fileIds, folderIds);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(() => clearSecondaryProgressData(operationId), TIMEOUT);
return Promise.reject(err);
});
};
@ -1421,6 +1427,8 @@ class UploadDataStore {
? data.conflictResolveType
: ConflictResolveType.Duplicate;
const operationId = uniqueid("operation_");
this.secondaryProgressDataStore.setSecondaryProgressBarData({
icon: isCopy ? "duplicate" : "move",
visible: true,
@ -1428,6 +1436,7 @@ class UploadDataStore {
label: isCopy ? translations.copy : translations.move,
alert: false,
filesCount: this.secondaryProgressDataStore.filesCount + fileIds.length,
operationId,
});
return isCopy
@ -1436,14 +1445,16 @@ class UploadDataStore {
folderIds,
fileIds,
conflictResolveType,
deleteAfter
deleteAfter,
operationId
)
: this.moveToAction(
destFolderId,
folderIds,
fileIds,
conflictResolveType,
deleteAfter
deleteAfter,
operationId
);
};
@ -1457,7 +1468,7 @@ class UploadDataStore {
let progress = data.progress;
if (!data) {
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(() => clearSecondaryProgressData(pbData.operationId), TIMEOUT);
return;
}
@ -1482,6 +1493,7 @@ class UploadDataStore {
visible: true,
alert: false,
currentFile: item,
operationId: pbData.operationId,
});
}
@ -1518,7 +1530,10 @@ class UploadDataStore {
if (!withPaging) {
!isCopy && removeFiles(fileIds, folderIds);
this.clearActiveOperations(fileIds, folderIds);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(
() => clearSecondaryProgressData(pbData.operationId),
TIMEOUT
);
this.dialogsStore.setIsFolderActions(false);
return;
}
@ -1535,7 +1550,10 @@ class UploadDataStore {
false
).finally(() => {
this.clearActiveOperations(fileIds, folderIds);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(
() => clearSecondaryProgressData(pbData.operationId),
TIMEOUT
);
this.dialogsStore.setIsFolderActions(false);
});
} else {
@ -1546,9 +1564,10 @@ class UploadDataStore {
percent: 100,
visible: true,
alert: false,
operationId: pbData.operationId,
});
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
setTimeout(() => clearSecondaryProgressData(pbData.operationId), TIMEOUT);
}
};