Merge branch 'develop' into bugfix/sharing-settings

# Conflicts:
#	products/ASC.Files/Client/src/store/files/actions.js
#	products/ASC.Files/Client/yarn.lock
#	products/ASC.People/Client/yarn.lock
#	web/ASC.Web.Client/yarn.lock
#	web/ASC.Web.Common/yarn.lock
#	web/ASC.Web.Components/yarn.lock
This commit is contained in:
Alexey Safronov 2020-12-08 18:06:31 +03:00
commit c8f7555017
26 changed files with 890 additions and 246 deletions

View File

@ -14,10 +14,11 @@ import { api, utils, toastr } from "asc-web-common";
import {
fetchFiles,
setTreeFolders,
setProgressBarData,
clearProgressData,
setSecondaryProgressBarData,
clearSecondaryProgressData,
setUpdateTree,
} from "../../../store/files/actions";
import { TIMEOUT } from "../../../helpers/constants";
import {
loopTreeFolders,
getSelectedFolderId,
@ -66,8 +67,8 @@ class DeleteDialogComponent extends React.Component {
treeFolders,
setTreeFolders,
isRecycleBinFolder,
setProgressBarData,
clearProgressData,
setSecondaryProgressBarData,
clearSecondaryProgressData,
t,
fetchFiles,
setUpdateTree,
@ -78,19 +79,23 @@ class DeleteDialogComponent extends React.Component {
.then((res) => {
const currentProcess = res.find((x) => x.id === id);
if (currentProcess && currentProcess.progress !== 100) {
setProgressBarData({
setSecondaryProgressBarData({
icon: "trash",
percent: currentProcess.progress,
label: t("DeleteOperation"),
visible: true,
alert: false,
});
setTimeout(() => this.loopDeleteOperation(id), 1000);
} else {
setProgressBarData({
setSecondaryProgressBarData({
icon: "trash",
percent: 100,
label: t("DeleteOperation"),
visible: true,
alert: false,
});
setTimeout(() => clearProgressData(), 5000);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
fetchFiles(currentFolderId, filter).then((data) => {
if (!isRecycleBinFolder && !!this.state.foldersList.length) {
const path = data.selectedFolder.pathParts.slice(0);
@ -106,8 +111,12 @@ class DeleteDialogComponent extends React.Component {
}
})
.catch((err) => {
toastr.error(err);
clearProgressData();
setSecondaryProgressBarData({
visible: true,
alert: true,
});
//toastr.error(err);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
});
};
@ -116,8 +125,8 @@ class DeleteDialogComponent extends React.Component {
isRecycleBinFolder,
onClose,
t,
setProgressBarData,
clearProgressData,
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = this.props;
const { selection } = this.state;
@ -139,10 +148,12 @@ class DeleteDialogComponent extends React.Component {
onClose();
if (folderIds.length || fileIds.length) {
setProgressBarData({
setSecondaryProgressBarData({
icon: "trash",
visible: true,
label: t("DeleteOperation"),
percent: 0,
alert: false,
});
files
@ -152,8 +163,12 @@ class DeleteDialogComponent extends React.Component {
this.loopDeleteOperation(id);
})
.catch((err) => {
toastr.error(err);
clearProgressData();
setSecondaryProgressBarData({
visible: true,
alert: true,
});
//toastr.error(err);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
});
}
};
@ -289,8 +304,8 @@ const mapStateToProps = (state) => {
export default connect(mapStateToProps, {
setTreeFolders,
setProgressBarData,
clearProgressData,
setSecondaryProgressBarData,
clearSecondaryProgressData,
setUpdateTree,
fetchFiles,
})(withRouter(DeleteDialog));

View File

@ -22,9 +22,10 @@ import {
getSelection,
} from "../../../store/files/selectors";
import {
setProgressBarData,
clearProgressData,
setSecondaryProgressBarData,
clearSecondaryProgressData,
} from "../../../store/files/actions";
import { TIMEOUT } from "../../../helpers/constants";
import DownloadContent from "./DownloadContent";
import { createI18N } from "../../../helpers/i18n";
const i18n = createI18N({
@ -178,8 +179,8 @@ class DownloadDialogComponent extends React.Component {
onDownloadProgress,
onClose,
t,
setProgressBarData,
clearProgressData,
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = this.props;
const downloadItems = this.getDownloadItems();
@ -187,10 +188,12 @@ class DownloadDialogComponent extends React.Component {
const folderIds = downloadItems[1];
if (fileConvertIds.length || folderIds.length) {
setProgressBarData({
setSecondaryProgressBarData({
icon: "file",
visible: true,
percent: 0,
label: t("ArchivingData"),
alert: false,
});
api.files
.downloadFormatFiles(fileConvertIds, folderIds)
@ -199,8 +202,12 @@ class DownloadDialogComponent extends React.Component {
onDownloadProgress(false);
})
.catch((err) => {
toastr.error(err);
clearProgressData();
setSecondaryProgressBarData({
visible: true,
alert: true,
});
//toastr.error(err);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
});
}
};
@ -629,6 +636,6 @@ const mapStateToProps = (state) => {
};
export default connect(mapStateToProps, {
setProgressBarData,
clearProgressData,
setSecondaryProgressBarData,
clearSecondaryProgressData,
})(withRouter(DownloadDialog));

View File

@ -7,9 +7,10 @@ import { withTranslation } from "react-i18next";
import { api, utils, toastr } from "asc-web-common";
import {
fetchFiles,
setProgressBarData,
clearProgressData,
setSecondaryProgressBarData,
clearSecondaryProgressData,
} from "../../../store/files/actions";
import { TIMEOUT } from "../../../helpers/constants";
import {
getSelectedFolderId,
getFilter,
@ -32,9 +33,9 @@ const EmptyTrashDialogComponent = (props) => {
t,
filter,
currentFolderId,
setProgressBarData,
setSecondaryProgressBarData,
isLoading,
clearProgressData,
clearSecondaryProgressData,
fetchFiles,
} = props;
@ -51,51 +52,65 @@ const EmptyTrashDialogComponent = (props) => {
const currentProcess = res.find((x) => x.id === id);
if (currentProcess && currentProcess.progress !== 100) {
const newProgressData = {
icon: "trash",
visible: true,
percent: currentProcess.progress,
label: t("DeleteOperation"),
alert: false,
};
setProgressBarData(newProgressData);
setSecondaryProgressBarData(newProgressData);
setTimeout(() => loopEmptyTrash(id), 1000);
} else {
fetchFiles(currentFolderId, filter)
.then(() => {
setProgressBarData({
setSecondaryProgressBarData({
icon: "trash",
visible: true,
percent: 100,
label: t("DeleteOperation"),
alert: false,
});
setTimeout(() => clearProgressData(), 5000);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
toastr.success(successMessage);
})
.catch((err) => {
toastr.error(err);
clearProgressData();
setSecondaryProgressBarData({
visible: true,
alert: true,
});
//toastr.error(err);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
});
}
})
.catch((err) => {
toastr.error(err);
clearProgressData();
setSecondaryProgressBarData({
visible: true,
alert: true,
});
//toastr.error(err);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
});
},
[
t,
currentFolderId,
filter,
setProgressBarData,
clearProgressData,
setSecondaryProgressBarData,
clearSecondaryProgressData,
fetchFiles,
]
);
const onEmptyTrash = useCallback(() => {
const newProgressData = {
icon: "trash",
visible: true,
percent: 0,
label: t("DeleteOperation"),
alert: false,
};
setProgressBarData(newProgressData);
setSecondaryProgressBarData(newProgressData);
onClose();
files
.emptyTrash()
@ -104,10 +119,20 @@ const EmptyTrashDialogComponent = (props) => {
loopEmptyTrash(id);
})
.catch((err) => {
toastr.error(err);
clearProgressData();
setSecondaryProgressBarData({
visible: true,
alert: true,
});
//toastr.error(err);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
});
}, [onClose, loopEmptyTrash, setProgressBarData, t, clearProgressData]);
}, [
onClose,
loopEmptyTrash,
setSecondaryProgressBarData,
t,
clearSecondaryProgressData,
]);
return (
<ModalDialogContainer>
@ -157,7 +182,7 @@ const mapStateToProps = (state) => {
};
export default connect(mapStateToProps, {
setProgressBarData,
clearProgressData,
setSecondaryProgressBarData,
clearSecondaryProgressData,
fetchFiles,
})(withRouter(EmptyTrashDialog));

View File

@ -13,18 +13,19 @@ import {
} from "asc-web-components";
import { constants, api, toastr, store as initStore } from "asc-web-common";
import {
clearProgressData,
clearSecondaryProgressData,
createFile,
createFolder,
fetchFiles,
renameFolder,
setIsLoading,
setNewRowItems,
setProgressBarData,
setSecondaryProgressBarData,
setTreeFolders,
setUpdateTree,
updateFile,
} from "../../../../../store/files/actions";
import { TIMEOUT } from "../../../../../helpers/constants";
import {
canConvert,
canWebEdit,
@ -357,33 +358,48 @@ class FilesRowContent extends React.PureComponent {
selectedFolder,
filter,
setIsLoading,
setProgressBarData,
setSecondaryProgressBarData,
t,
clearProgressData,
clearSecondaryProgressData,
fetchFiles,
} = this.props;
api.files.getConvertFile(fileId).then((res) => {
if (res && res[0] && res[0].progress !== 100) {
setProgressBarData({
setSecondaryProgressBarData({
icon: "file",
visible: true,
percent: res[0].progress,
label: t("Convert"),
alert: false,
});
setTimeout(() => this.getConvertProgress(fileId), 1000);
} else {
if (res[0].error) {
setSecondaryProgressBarData({
visible: true,
alert: true,
});
toastr.error(res[0].error);
clearProgressData();
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
} else {
setProgressBarData({
setSecondaryProgressBarData({
icon: "file",
visible: true,
percent: 100,
label: t("Convert"),
alert: false,
});
setTimeout(() => clearProgressData(), 5000);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
const newFilter = filter.clone();
fetchFiles(selectedFolder.id, newFilter)
.catch((err) => toastr.error(err))
.catch((err) => {
setSecondaryProgressBarData({
visible: true,
alert: true,
});
//toastr.error(err);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
})
.finally(() => setIsLoading(false));
}
}
@ -391,8 +407,14 @@ class FilesRowContent extends React.PureComponent {
};
onConvert = () => {
const { item, t, setProgressBarData } = this.props;
setProgressBarData({ visible: true, percent: 0, label: t("Convert") });
const { item, t, setSecondaryProgressBarData } = this.props;
setSecondaryProgressBarData({
icon: "file",
visible: true,
percent: 0,
label: t("Convert"),
alert: false,
});
this.setState({ showConvertDialog: false }, () =>
api.files.convertFile(item.id).then((convertRes) => {
if (convertRes && convertRes[0] && convertRes[0].progress !== 100) {
@ -683,10 +705,10 @@ export default connect(mapStateToProps, {
updateFile,
renameFolder,
setTreeFolders,
setProgressBarData,
setSecondaryProgressBarData,
setUpdateTree,
setNewRowItems,
setIsLoading,
clearProgressData,
clearSecondaryProgressData,
fetchFiles,
})(withRouter(withTranslation()(FilesRowContent)));

View File

@ -31,7 +31,7 @@ import {
store,
} from "asc-web-common";
import {
clearProgressData,
clearSecondaryProgressData,
loopFilesOperations,
markItemAsFavorite,
removeItemFromFavorite,
@ -46,7 +46,7 @@ import {
setIsLoading,
setMediaViewerData,
setUpdateTree,
setProgressBarData,
setSecondaryProgressBarData,
setSelected,
setSelection,
setTreeFolders,
@ -54,6 +54,7 @@ import {
addFileToRecentlyViewed,
setSharingPanelVisible,
} from "../../../../../store/files/actions";
import { TIMEOUT } from "../../../../../helpers/constants";
import {
getCurrentFolderCount,
getDragging,
@ -364,11 +365,17 @@ class SectionBodyContent extends React.Component {
};
onDeleteFile = (fileId, currentFolderId) => {
const { t, setProgressBarData, clearProgressData } = this.props;
setProgressBarData({
const {
t,
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = this.props;
setSecondaryProgressBarData({
icon: "trash",
visible: true,
percent: 0,
label: t("DeleteOperation"),
alert: false,
});
api.files
.deleteFile(fileId)
@ -377,8 +384,12 @@ class SectionBodyContent extends React.Component {
this.loopDeleteProgress(id, currentFolderId, false);
})
.catch((err) => {
toastr.error(err);
clearProgressData();
setSecondaryProgressBarData({
visible: true,
alert: true,
});
//toastr.error(err);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
});
};
@ -389,24 +400,28 @@ class SectionBodyContent extends React.Component {
setTreeFolders,
isRecycleBin,
t,
setProgressBarData,
setSecondaryProgressBarData,
fetchFiles,
setUpdateTree,
} = this.props;
api.files.getProgress().then((res) => {
const deleteProgress = res.find((x) => x.id === id);
if (deleteProgress && deleteProgress.progress !== 100) {
setProgressBarData({
setSecondaryProgressBarData({
icon: "trash",
visible: true,
percent: deleteProgress.progress,
label: t("DeleteOperation"),
alert: false,
});
setTimeout(() => this.loopDeleteProgress(id, folderId, isFolder), 1000);
} else {
setProgressBarData({
setSecondaryProgressBarData({
icon: "trash",
visible: true,
percent: 100,
label: t("DeleteOperation"),
alert: false,
});
fetchFiles(folderId, filter)
.then((data) => {
@ -424,20 +439,34 @@ class SectionBodyContent extends React.Component {
: toastr.success(`File moved to recycle bin`);
})
.catch((err) => {
toastr.error(err);
this.props.clearProgressData();
setSecondaryProgressBarData({
visible: true,
alert: true,
});
//toastr.error(err);
setTimeout(() => this.props.clearSecondaryProgressData(), TIMEOUT);
})
.finally(() =>
setTimeout(() => this.props.clearProgressData(), 5000)
setTimeout(() => this.props.clearSecondaryProgressData(), TIMEOUT)
);
}
});
};
onDeleteFolder = (folderId, currentFolderId) => {
const { t, setProgressBarData, clearProgressData } = this.props;
const {
t,
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = this.props;
const progressLabel = t("DeleteOperation");
setProgressBarData({ visible: true, percent: 0, label: progressLabel });
setSecondaryProgressBarData({
icon: "trash",
visible: true,
percent: 0,
label: progressLabel,
alert: false,
});
api.files
.deleteFolder(folderId, currentFolderId)
.then((res) => {
@ -445,8 +474,12 @@ class SectionBodyContent extends React.Component {
this.loopDeleteProgress(id, currentFolderId, true);
})
.catch((err) => {
toastr.error(err);
clearProgressData();
setSecondaryProgressBarData({
visible: true,
alert: true,
});
//toastr.error(err);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
});
};
@ -547,7 +580,12 @@ class SectionBodyContent extends React.Component {
onCopyAction = () =>
this.setState({ showCopyPanel: !this.state.showCopyPanel });
onDuplicate = () => {
const { selection, selectedFolderId, setProgressBarData, t } = this.props;
const {
selection,
selectedFolderId,
setSecondaryProgressBarData,
t,
} = this.props;
const folderIds = [];
const fileIds = [];
selection[0].fileExst
@ -556,10 +594,12 @@ class SectionBodyContent extends React.Component {
const conflictResolveType = 0; //Skip = 0, Overwrite = 1, Duplicate = 2
const deleteAfter = false;
setProgressBarData({
setSecondaryProgressBarData({
icon: "duplicate",
visible: true,
percent: 0,
label: t("CopyOperation"),
alert: false,
});
this.copyTo(
selectedFolderId,
@ -1306,17 +1346,19 @@ class SectionBodyContent extends React.Component {
isShare,
isCommon,
isAdmin,
setProgressBarData,
setSecondaryProgressBarData,
} = this.props;
const folderIds = [];
const fileIds = [];
const conflictResolveType = 0; //Skip = 0, Overwrite = 1, Duplicate = 2
const deleteAfter = true;
setProgressBarData({
setSecondaryProgressBarData({
icon: "move",
visible: true,
percent: 0,
label: t("MoveToOperation"),
alert: false,
});
for (let item of selection) {
if (item.fileExst) {
@ -1372,7 +1414,7 @@ class SectionBodyContent extends React.Component {
conflictResolveType,
deleteAfter
) => {
const { loopFilesOperations, clearProgressData } = this.props;
const { loopFilesOperations, clearSecondaryProgressData } = this.props;
api.files
.copyToFolder(
@ -1387,8 +1429,12 @@ class SectionBodyContent extends React.Component {
loopFilesOperations(id, destFolderId, true);
})
.catch((err) => {
toastr.error(err);
clearProgressData();
setSecondaryProgressBarData({
visible: true,
alert: true,
});
//toastr.error(err);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
});
};
@ -1399,7 +1445,7 @@ class SectionBodyContent extends React.Component {
conflictResolveType,
deleteAfter
) => {
const { loopFilesOperations, clearProgressData } = this.props;
const { loopFilesOperations, clearSecondaryProgressData } = this.props;
api.files
.moveToFolder(
@ -1414,8 +1460,12 @@ class SectionBodyContent extends React.Component {
loopFilesOperations(id, destFolderId, false);
})
.catch((err) => {
toastr.error(err);
clearProgressData();
setSecondaryProgressBarData({
visible: true,
alert: true,
});
//toastr.error(err);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
});
};
@ -1852,12 +1902,12 @@ export default connect(mapStateToProps, {
setDragging,
setDragItem,
setMediaViewerData,
setProgressBarData,
setSecondaryProgressBarData,
setSelection,
setSelected,
setUpdateTree,
setIsLoading,
clearProgressData,
clearSecondaryProgressData,
markItemAsFavorite,
removeItemFromFavorite,
fetchFavoritesFolder,

View File

@ -22,12 +22,13 @@ import {
import {
fetchFiles,
setAction,
setProgressBarData,
clearProgressData,
setSecondaryProgressBarData,
clearSecondaryProgressData,
setIsLoading,
setSelected,
setSharingPanelVisible,
} from "../../../../../store/files/actions";
import { TIMEOUT } from "../../../../../helpers/constants";
import {
EmptyTrashDialog,
DeleteDialog,
@ -230,25 +231,36 @@ class SectionHeaderContent extends React.Component {
.getProgress()
.then((res) => {
if (!url) {
this.props.setProgressBarData({
this.props.setSecondaryProgressBarData({
icon: "file",
visible: true,
percent: res[0].progress,
label: this.props.t("ArchivingData"),
alert: false,
});
setTimeout(() => this.loop(res[0].url), 1000);
} else {
setTimeout(() => this.props.clearProgressData(), 5000);
setTimeout(() => this.props.clearSecondaryProgressData(), TIMEOUT);
return window.open(url, "_blank");
}
})
.catch((err) => {
toastr.error(err);
this.props.clearProgressData();
setSecondaryProgressBarData({
visible: true,
alert: true,
});
//toastr.error(err);
setTimeout(() => this.props.clearSecondaryProgressData(), TIMEOUT);
});
};
downloadAction = () => {
const { t, selection, setProgressBarData, clearProgressData } = this.props;
const {
t,
selection,
setSecondaryProgressBarData,
clearSecondaryProgressData,
} = this.props;
const fileIds = [];
const folderIds = [];
const items = [];
@ -263,10 +275,12 @@ class SectionHeaderContent extends React.Component {
}
}
setProgressBarData({
setSecondaryProgressBarData({
icon: "file",
visible: true,
percent: 0,
label: t("ArchivingData"),
alert: false,
});
api.files
@ -275,8 +289,12 @@ class SectionHeaderContent extends React.Component {
this.loop(res[0].url);
})
.catch((err) => {
toastr.error(err);
clearProgressData();
setSecondaryProgressBarData({
visible: true,
alert: true,
});
//toastr.error(err);
setTimeout(() => clearSecondaryProgressData(), TIMEOUT);
});
};
@ -658,9 +676,9 @@ const mapStateToProps = (state) => {
export default connect(mapStateToProps, {
setAction,
setProgressBarData,
setSecondaryProgressBarData,
setIsLoading,
clearProgressData,
clearSecondaryProgressData,
fetchFiles,
setSelected,
setSharingPanelVisible,

View File

@ -30,7 +30,8 @@ import {
getSelectedFolderId,
getFileActionId,
getFilter,
getProgressData,
getPrimaryProgressData,
getSecondaryProgressData,
getTreeFolders,
getViewAs,
getIsLoading,
@ -169,7 +170,8 @@ class PureHome extends React.Component {
render() {
//console.log("Home render");
const {
progressData,
primaryProgressData,
secondaryProgressData,
viewAs,
convertDialogVisible,
sharingPanelVisible,
@ -192,12 +194,20 @@ class PureHome extends React.Component {
onDrop={this.onDrop}
setSelections={this.props.setSelections}
onMouseMove={this.onMouseMove}
showProgressBar={progressData.visible}
progressBarValue={progressData.percent}
//progressBarDropDownContent={progressBarContent}
progressBarLabel={progressData.label}
showPrimaryProgressBar={primaryProgressData.visible}
primaryProgressBarValue={primaryProgressData.percent}
primaryProgressBarIcon={primaryProgressData.icon}
showPrimaryButtonAlert={primaryProgressData.alert}
showSecondaryProgressBar={secondaryProgressData.visible}
secondaryProgressBarValue={secondaryProgressData.percent}
secondaryProgressBarIcon={secondaryProgressData.icon}
showSecondaryButtonAlert={secondaryProgressData.alert}
viewAs={viewAs}
hideAside={!!fileActionId || progressData.visible}
hideAside={
!!fileActionId ||
primaryProgressData.visible ||
secondaryProgressData.visible
}
isLoaded={isLoaded}
>
<PageLayout.ArticleHeader>
@ -260,7 +270,8 @@ function mapStateToProps(state) {
fileActionId: getFileActionId(state),
filter: getFilter(state),
isRecycleBin: getIsRecycleBinFolder(state),
progressData: getProgressData(state),
primaryProgressData: getPrimaryProgressData(state),
secondaryProgressData: getSecondaryProgressData(state),
treeFolders: getTreeFolders(state),
viewAs: getViewAs(state),
isLoading: getIsLoading(state),

View File

@ -8,8 +8,8 @@ import { utils as commonUtils, toastr } from "asc-web-common";
import { StyledAsidePanel } from "../StyledPanels";
import TreeFolders from "../../Article/Body/TreeFolders";
import {
setProgressBarData,
itemOperationToFolder
setSecondaryProgressBarData,
itemOperationToFolder,
} from "../../../store/files/actions";
import {
getFilter,
@ -39,10 +39,10 @@ class OperationsPanelComponent extends React.Component {
t,
isCopy,
selection,
setProgressBarData,
setSecondaryProgressBarData,
currentFolderId,
onClose,
itemOperationToFolder
itemOperationToFolder,
} = this.props;
const destFolderId = Number(e);
@ -53,8 +53,7 @@ class OperationsPanelComponent extends React.Component {
if (currentFolderId === destFolderId) {
return onClose();
}
else {
} else {
for (let item of selection) {
if (item.fileExst) {
fileIds.push(item.id);
@ -65,12 +64,21 @@ class OperationsPanelComponent extends React.Component {
}
}
onClose();
setProgressBarData({
setSecondaryProgressBarData({
icon: isCopy ? "duplicate" : "move",
visible: true,
percent: 0,
label: isCopy ? t("CopyOperation") : t("MoveToOperation"),
alert: false,
});
itemOperationToFolder(destFolderId, folderIds, fileIds, conflictResolveType, deleteAfter, isCopy)
itemOperationToFolder(
destFolderId,
folderIds,
fileIds,
conflictResolveType,
deleteAfter,
isCopy
);
}
};
@ -83,7 +91,7 @@ class OperationsPanelComponent extends React.Component {
isRecycleBin,
visible,
onClose,
operationsFolders
operationsFolders,
} = this.props;
const zIndex = 310;
const expandedKeys = this.props.expandedKeys.map((item) => item.toString());
@ -134,11 +142,11 @@ const mapStateToProps = (state) => {
expandedKeys: getPathParts(state),
currentFolderId: getSelectedFolderId(state),
isRecycleBin: getIsRecycleBinFolder(state),
operationsFolders: getOperationsFolders(state)
operationsFolders: getOperationsFolders(state),
};
};
export default connect(mapStateToProps, {
setProgressBarData,
itemOperationToFolder
setSecondaryProgressBarData,
itemOperationToFolder,
})(withRouter(OperationsPanel));

View File

@ -10,3 +10,4 @@ export const PAGE = "page";
export const PAGE_COUNT = "count";
export const FOLDER = "folder";
export const PREVIEW = "preview";
export const TIMEOUT = 5000;

View File

@ -12,6 +12,7 @@ import {
SORT_ORDER,
FOLDER,
PREVIEW,
TIMEOUT,
} from "../../helpers/constants";
import config from "../../../package.json";
import {
@ -21,12 +22,16 @@ import {
getSelectedFolderId,
getFilter,
getIsRecycleBinFolder,
getProgressData,
getPrimaryProgressData,
getSecondaryProgressData,
getTreeFolders,
getSettingsTree,
getPrivacyFolder,
} from "./selectors";
import sumBy from "lodash/sumBy";
import throttle from "lodash/throttle";
const { files, FilesFilter } = api;
const { FolderType } = constants;
const { isEncryptionSupport } = store.auth.selectors;
@ -47,7 +52,9 @@ export const SET_ACTION = "SET_ACTION";
export const SET_DRAGGING = "SET_DRAGGING";
export const SET_DRAG_ITEM = "SET_DRAG_ITEM";
export const SET_MEDIA_VIEWER_VISIBLE = "SET_MEDIA_VIEWER_VISIBLE";
export const SET_PROGRESS_BAR_DATA = "SET_PROGRESS_BAR_DATA";
export const SET_PRIMARY_PROGRESS_BAR_DATA = "SET_PRIMARY_PROGRESS_BAR_DATA";
export const SET_SECONDARY_PROGRESS_BAR_DATA =
"SET_SECONDARY_PROGRESS_BAR_DATA";
export const SET_VIEW_AS = "SET_VIEW_AS";
export const SET_CONVERT_DIALOG_VISIBLE = "SET_CONVERT_DIALOG_VISIBLE";
export const SET_SHARING_PANEL_VISIBLE = "SET_SHARING_PANEL_VISIBLE";
@ -182,10 +189,17 @@ export function setMediaViewerData(mediaViewerData) {
};
}
export function setProgressBarData(progressData) {
export function setPrimaryProgressBarData(primaryProgressData) {
return {
type: SET_PROGRESS_BAR_DATA,
progressData,
type: SET_PRIMARY_PROGRESS_BAR_DATA,
primaryProgressData,
};
}
export function setSecondaryProgressBarData(secondaryProgressData) {
return {
type: SET_SECONDARY_PROGRESS_BAR_DATA,
secondaryProgressData,
};
}
@ -312,7 +326,7 @@ export function setFilterUrl(filter) {
}
// TODO: similar to fetchFolder, remove one
export function fetchFiles(folderId, filter) {
export function fetchFiles(folderId, filter, clearFilter = true) {
return (dispatch, getState) => {
const filterData = filter ? filter.clone() : FilesFilter.getDefault();
filterData.folder = folderId;
@ -330,7 +344,7 @@ export function fetchFiles(folderId, filter) {
);
filterData.total = 0;
dispatch(setFilesFilter(filterData));
if (clearFilter) {
dispatch(setFolders([]));
dispatch(setFiles([]));
dispatch(setSelected("close"));
@ -342,6 +356,7 @@ export function fetchFiles(folderId, filter) {
...{ new: 0 },
})
);
}
return Promise.resolve();
}
}
@ -358,7 +373,9 @@ export function fetchFiles(folderId, filter) {
dispatch(
setFiles(isPrivacyFolder && !isEncryptionSupport ? [] : data.files)
);
if (clearFilter) {
dispatch(setSelected("close"));
}
return dispatch(
setSelectedFolder({
folders: data.folders,
@ -570,9 +587,31 @@ export function getShareUsers(folderIds, fileIds) {
return axios.all(requests).then((res) => res);
}
export function clearProgressData() {
export function clearPrimaryProgressData() {
return (dispatch) => {
dispatch(setProgressBarData({ visible: false, percent: 0, label: "" }));
dispatch(
setPrimaryProgressBarData({
visible: false,
percent: 0,
label: "",
icon: "",
alert: false,
})
);
};
}
export function clearSecondaryProgressData() {
return (dispatch) => {
dispatch(
setSecondaryProgressBarData({
visible: false,
percent: 0,
label: "",
icon: "",
alert: false,
})
);
};
}
@ -660,7 +699,8 @@ export function getFilesSettings() {
export const startUpload = (uploadFiles, folderId, t) => {
return (dispatch, getState) => {
const state = getState();
const newFiles = [];
console.log("start upload", state.files.uploadData);
let newFiles = state.files.uploadData.files;
let filesSize = 0;
const convertFiles = [];
let convertFilesSize = 0;
@ -671,30 +711,37 @@ export const startUpload = (uploadFiles, folderId, t) => {
const parts = item.name.split(".");
const ext = parts.length > 1 ? "." + parts.pop() : "";
if (canConvert(ext)(state)) {
convertFiles.push(item);
convertFiles.push({ file: item, toFolderId: folderId });
convertFilesSize += item.size;
} else {
newFiles.push(item);
newFiles.push({ file: item, toFolderId: folderId });
filesSize += item.size;
}
} else {
toastr.error(t("ErrorUploadMessage"));
dispatch(
setPrimaryProgressBarData({
visible: true,
alert: true,
})
);
setTimeout(() => dispatch(clearPrimaryProgressData()), TIMEOUT);
//toastr.error(t("ErrorUploadMessage"));
}
}
const uploadStatus = convertFiles.length ? "pending" : null;
const uploadToFolder = folderId;
const showConvertDialog = !!convertFiles.length;
const percent = state.files.uploadData.percent;
const uploadedFiles = state.files.uploadData.uploadedFiles;
console.log("newFiles: ", newFiles);
const newUploadData = {
files: newFiles,
filesSize,
convertFiles,
convertFilesSize,
uploadStatus,
uploadToFolder,
uploadedFiles: 0,
percent: 0,
uploadedFiles,
percent,
uploaded: false,
};
dispatch(setUploadData(newUploadData));
@ -702,7 +749,7 @@ export const startUpload = (uploadFiles, folderId, t) => {
if (showConvertDialog) {
dispatch(setConvertDialogVisible(showConvertDialog));
}
if (state.files.uploadData.uploaded)
startUploadFiles(
t,
newFiles.length,
@ -721,42 +768,58 @@ const startUploadFiles = (
getState
) => {
if (filesLength > 0 || convertFilesLength > 0) {
const progressData = { visible: true, percent: 0, label: "" };
const state = getState();
const percent = state.files.uploadData.percent;
const progressData = {
visible: true,
percent,
label: "",
icon: "upload",
alert: false,
};
progressData.label = t("UploadingLabel", {
file: 0,
totalFiles: filesLength + convertFilesLength,
});
dispatch(setProgressBarData(progressData));
dispatch(setPrimaryProgressBarData(progressData));
startSessionFunc(0, t, dispatch, getState);
}
};
const chunkSize = 1024 * 1023; //~0.999mb
const throttleRefreshFiles = throttle((toFolderId, dispatch, getState) => {
refreshFiles(toFolderId, dispatch, getState);
}, 10000);
const startSessionFunc = (indexOfFile, t, dispatch, getState) => {
const state = getState();
const { uploadData } = state.files;
const { uploaded, uploadToFolder, files, convertFiles } = uploadData;
const { uploaded, files, convertFiles } = uploadData;
const currentFiles = uploaded ? convertFiles : files;
console.log("START UPLOAD SESSION FUNC", uploadData);
if (!uploaded && files.length === 0) {
uploadData.uploaded = true;
dispatch(setUploadData(uploadData));
return;
}
let item = files[indexOfFile] || { file: null, toFolderId: null };
let { file, toFolderId } = item;
let file = files[indexOfFile];
let isLatestFile = indexOfFile === files.length - 1;
//let isLatestFile = indexOfFile === files.length - 1;
if (uploaded) {
if (convertFiles.length) {
file = convertFiles[indexOfFile];
isLatestFile = indexOfFile === convertFiles.length - 1;
let item = convertFiles[indexOfFile] || { file: null, toFolderId: null };
file = item.file;
toFolderId = item.toFolderId;
//isLatestFile = indexOfFile === convertFiles.length - 1;
} else {
//Test return empty convert files
return;
}
}
const fileName = file.name;
const fileSize = file.size;
const relativePath = file.path
@ -767,12 +830,11 @@ const startSessionFunc = (indexOfFile, t, dispatch, getState) => {
let location;
const requestsDataArray = [];
const chunkSize = 1024 * 1023; //~0.999mb
const chunks = Math.ceil(file.size / chunkSize, chunkSize);
let chunk = 0;
api.files
.startUploadSession(uploadToFolder, fileName, fileSize, relativePath)
.startUploadSession(toFolderId, fileName, fileSize, relativePath)
.then((res) => {
location = res.data.location;
while (chunk < chunks) {
@ -783,21 +845,28 @@ const startSessionFunc = (indexOfFile, t, dispatch, getState) => {
chunk++;
}
})
.then(() =>
.then(() => {
throttleRefreshFiles(toFolderId, dispatch, getState);
//TODO: rewrite to async function
sendChunk(
currentFiles,
location,
requestsDataArray,
isLatestFile,
indexOfFile,
t,
dispatch,
getState
)
)
);
})
.catch((err) => {
toastr.error(err);
dispatch(clearProgressData());
dispatch(
setPrimaryProgressBarData({
visible: true,
alert: true,
})
);
//toastr.error(err);
setTimeout(() => dispatch(clearPrimaryProgressData()), TIMEOUT);
});
};
@ -805,7 +874,6 @@ const sendChunk = (
files,
location,
requestsDataArray,
isLatestFile,
indexOfFile,
t,
dispatch,
@ -817,34 +885,57 @@ const sendChunk = (
uploaded,
percent,
uploadedFiles,
uploadToFolder,
filesSize,
convertFilesSize,
} = uploadData;
const totalSize = convertFilesSize + filesSize;
//const totalSize = convertFilesSize + filesSize;
let newPercent = percent;
const toFolderId = uploadData.files[indexOfFile].toFolderId;
const sendRequestFunc = (index) => {
api.files
.uploadFile(location, requestsDataArray[index])
.then((res) => {
//percent problem? use getState()
const currentFile = files[indexOfFile];
const newState = getState();
const newFilesLength = newState.files.uploadData.files.length;
const newTotalSize = sumBy(
newState.files.uploadData.files,
(f) => f.file.size
);
//console.log("newTotalSize ", newTotalSize);
let isLatestFile = indexOfFile === newFilesLength - 1;
const fileId = res.data.data.id;
const newPercent = percent + (currentFile.size / totalSize) * 100;
const totalUploadedFiles = newState.files.uploadData.files.filter(
(_, i) => i < indexOfFile
);
if (res.data.data && res.data.data.uploaded) {
//newState = { percent: newPercent };
//console.log("indexOfFile ", indexOfFile);
//console.log("totalUploadedFiles ", totalUploadedFiles);
const totalUploadedSize = sumBy(totalUploadedFiles, (f) => f.file.size);
//console.log("totalUploadedSize ", totalUploadedSize);
if (index < requestsDataArray.length) {
//newPercent = (index / requestsDataArray.length) * 100;
newPercent =
((index * chunkSize + totalUploadedSize) / newTotalSize) * 100;
}
/*if (res.data.data && res.data.data.uploaded) {
newPercent = (currentFile.size / newTotalSize) * 100;
}*/
//console.log("newPercent", newPercent);
if (index + 1 !== requestsDataArray.length) {
dispatch(
setProgressBarData({
setPrimaryProgressBarData({
icon: "upload",
label: t("UploadingLabel", {
file: uploadedFiles,
totalFiles: files.length,
}),
newPercent,
percent: newPercent,
visible: true,
alert: false,
})
);
sendRequestFunc(index + 1);
@ -865,7 +956,7 @@ const sendChunk = (
});
} else if (isLatestFile) {
if (uploaded) {
updateFiles(uploadToFolder, dispatch, getState);
updateFiles(toFolderId, dispatch, getState);
} else {
const uploadStatus = getState().files.uploadData.uploadStatus;
if (uploadStatus === "convert") {
@ -897,7 +988,7 @@ const sendChunk = (
...{ uploadedFiles: uploadedFiles + 1, percent: newPercent },
};
updateConvertProgress(newUploadData, t, dispatch);
updateFiles(uploadToFolder, dispatch, getState);
updateFiles(toFolderId, dispatch, getState);
}
}
} else {
@ -906,33 +997,65 @@ const sendChunk = (
...{ uploadedFiles: uploadedFiles + 1, percent: newPercent },
};
updateConvertProgress(newUploadData, t, dispatch);
console.log("Start session func ", newUploadData, indexOfFile + 1);
startSessionFunc(indexOfFile + 1, t, dispatch, getState);
}
})
.catch((err) => toastr.error(err));
.catch((err) => {
dispatch(
setPrimaryProgressBarData({
visible: true,
alert: true,
})
);
//toastr.error(err);
setTimeout(() => dispatch(clearPrimaryProgressData()), TIMEOUT);
});
};
sendRequestFunc(0);
};
const updateFiles = (folderId, dispatch, getState) => {
const { files } = getState();
const { filter, treeFolders, selectedFolder } = files;
//console.log("folderId ", folderId);
const uploadData = {
files: [],
filesSize: 0,
convertFiles: [],
convertFilesSize: 0,
uploadStatus: null,
uploadToFolder: null,
uploadedFiles: 0,
percent: 0,
uploaded: true,
};
return refreshFiles(folderId, dispatch, getState)
.catch((err) => {
dispatch(
setPrimaryProgressBarData({
alert: true,
visible: true,
})
);
setTimeout(() => dispatch(clearPrimaryProgressData()), TIMEOUT);
//toastr.error(err);
})
.finally(() =>
setTimeout(() => {
dispatch(clearPrimaryProgressData());
dispatch(setUploadData(uploadData));
}, TIMEOUT)
);
};
if (selectedFolder.id === folderId) {
return dispatch(fetchFiles(selectedFolder.id, filter.clone()))
.then((data) => {
const refreshFiles = (folderId, dispatch, getState) => {
const { files } = getState();
const { filter, treeFolders, selectedFolder } = files;
if (
selectedFolder.id === folderId &&
window.location.pathname.indexOf("/history") === -1
) {
return dispatch(fetchFiles(selectedFolder.id, filter.clone(), false)).then(
(data) => {
const path = data.selectedFolder.pathParts;
const newTreeFolders = treeFolders;
const folders = data.selectedFolder.folders;
@ -940,18 +1063,10 @@ const updateFiles = (folderId, dispatch, getState) => {
loopTreeFolders(path, newTreeFolders, folders, foldersCount);
dispatch(setTreeFolders(newTreeFolders));
dispatch(setUpdateTree(true));
})
.catch((err) => toastr.error(err))
.finally(() =>
setTimeout(() => {
dispatch(clearProgressData());
dispatch(setUploadData(uploadData));
}, 5000)
}
);
} else {
return api.files
.getFolder(folderId, filter.clone())
.then((data) => {
return api.files.getFolder(folderId, filter.clone()).then((data) => {
const path = data.pathParts;
const newTreeFolders = treeFolders;
const folders = data.folders;
@ -959,14 +1074,7 @@ const updateFiles = (folderId, dispatch, getState) => {
loopTreeFolders(path, newTreeFolders, folders, foldersCount);
dispatch(setTreeFolders(newTreeFolders));
dispatch(setUpdateTree(true));
})
.catch((err) => toastr.error(err))
.finally(() =>
setTimeout(() => {
dispatch(clearProgressData());
dispatch(setUploadData(uploadData));
}, 5000)
);
});
}
};
@ -979,7 +1087,7 @@ const getConvertProgress = (
dispatch,
getState
) => {
const { uploadedFiles, uploadToFolder } = uploadData;
const { uploadedFiles } = uploadData;
api.files.getConvertFile(fileId).then((res) => {
if (res && res[0] && res[0].progress !== 100) {
setTimeout(
@ -1001,10 +1109,18 @@ const getConvertProgress = (
!isLatestFile && startSessionFunc(indexOfFile + 1, t, dispatch, getState);
if (res[0].error) {
toastr.error(res[0].error);
dispatch(
setPrimaryProgressBarData({
visible: true,
alert: true,
})
);
setTimeout(() => dispatch(clearPrimaryProgressData()), TIMEOUT);
//toastr.error(res[0].error);
}
if (isLatestFile) {
updateFiles(uploadToFolder, dispatch, getState);
const toFolderId = uploadData.files[indexOfFile].toFolderId;
updateFiles(toFolderId, dispatch, getState);
return;
}
}
@ -1033,27 +1149,23 @@ const updateConvertProgress = (uploadData, t, dispatch) => {
dispatch(setUploadData(uploadData));
dispatch(
setProgressBarData({
setPrimaryProgressBarData({
icon: "upload",
label: t("UploadingLabel", { file, totalFiles }),
percent,
visible: true,
alert: false,
})
);
if (!progressVisible) {
setTimeout(() => dispatch(clearProgressData()), 5000);
setTimeout(() => dispatch(clearPrimaryProgressData()), TIMEOUT);
}
};
export const setDialogVisible = (t) => {
return (dispatch, getState) => {
const { uploadData } = getState().files;
const {
files,
uploadStatus,
uploadToFolder,
uploadedFiles,
percent,
} = uploadData;
const { files, uploadStatus, uploadedFiles, percent } = uploadData;
dispatch(setConvertDialogVisible(false));
const label = t("UploadingLabel", {
@ -1062,15 +1174,30 @@ export const setDialogVisible = (t) => {
});
if (uploadStatus === null) {
dispatch(setProgressBarData({ label, percent: 100, visible: true }));
dispatch(
setPrimaryProgressBarData({
icon: "upload",
label,
percent: 100,
visible: true,
alert: false,
})
);
uploadData.uploadedFiles = 0;
uploadData.percent = 0;
dispatch(setUploadData(uploadData));
updateFiles(uploadToFolder, dispatch, getState);
} else if (!files.length) {
dispatch(clearProgressData());
dispatch(clearPrimaryProgressData());
} else {
dispatch(setProgressBarData({ label, percent, visible: true }));
dispatch(
setPrimaryProgressBarData({
icon: "upload",
label,
percent,
visible: true,
alert: false,
})
);
uploadData.uploadStatus = "cancel";
dispatch(setUploadData(uploadData));
}
@ -1168,7 +1295,7 @@ export const loopFilesOperations = (id, destFolderId, isCopy) => {
const currentFolderId = getSelectedFolderId(state);
const filter = getFilter(state);
const isRecycleBin = getIsRecycleBinFolder(state);
const progressData = getProgressData(state);
const progressData = getSecondaryProgressData(state);
const treeFolders = getTreeFolders(state);
const loopOperation = () => {
@ -1178,19 +1305,23 @@ export const loopFilesOperations = (id, destFolderId, isCopy) => {
const currentItem = res.find((x) => x.id === id);
if (currentItem && currentItem.progress !== 100) {
dispatch(
setProgressBarData({
setSecondaryProgressBarData({
icon: "move",
label: progressData.label,
percent: currentItem.progress,
visible: true,
alert: false,
})
);
setTimeout(() => loopOperation(), 1000);
} else {
dispatch(
setProgressBarData({
setSecondaryProgressBarData({
icon: "move",
label: progressData.label,
percent: 100,
visible: true,
alert: false,
})
);
api.files
@ -1222,36 +1353,68 @@ export const loopFilesOperations = (id, destFolderId, isCopy) => {
})
.catch((err) => {
console.log("ERROR_1", err);
toastr.error(err);
dispatch(clearProgressData());
dispatch(
setPrimaryProgressBarData({
visible: true,
alert: true,
})
);
//toastr.error(err);
setTimeout(
() => dispatch(clearPrimaryProgressData()),
TIMEOUT
);
})
.finally(() =>
setTimeout(() => dispatch(clearProgressData()), 5000)
setTimeout(
() => dispatch(clearPrimaryProgressData()),
TIMEOUT
)
);
} else {
dispatch(
setProgressBarData({
setSecondaryProgressBarData({
icon: "duplicate",
label: progressData.label,
percent: 100,
visible: true,
alert: false,
})
);
setTimeout(() => dispatch(clearProgressData()), 5000);
setTimeout(
() => dispatch(clearSecondaryProgressData()),
TIMEOUT
);
dispatch(setUpdateTree(true));
dispatch(setTreeFolders(newTreeFolders));
}
})
.catch((err) => {
console.log("ERROR_2", err);
toastr.error(err);
dispatch(clearProgressData());
dispatch(
setSecondaryProgressBarData({
visible: true,
alert: true,
})
);
//toastr.error(err);
setTimeout(
() => dispatch(clearSecondaryProgressData()),
TIMEOUT
);
});
}
})
.catch((err) => {
console.log("ERROR_3", err);
toastr.error(err);
dispatch(clearProgressData());
dispatch(
setSecondaryProgressBarData({
visible: true,
alert: true,
})
);
//toastr.error(err);
setTimeout(() => dispatch(clearSecondaryProgressData()), TIMEOUT);
});
};
@ -1310,8 +1473,15 @@ export function itemOperationToFolder(
dispatch(loopFilesOperations(id, destFolderId, isCopy));
})
.catch((err) => {
toastr.error(err);
dispatch(clearProgressData());
dispatch(
setPrimaryProgressBarData({
visible: true,
alert: true,
})
);
//toastr.error(err);
setTimeout(() => dispatch(clearPrimaryProgressData()), TIMEOUT);
setTimeout(() => dispatch(clearSecondaryProgressData()), TIMEOUT);
});
};
}

View File

@ -16,7 +16,8 @@ import {
SET_DRAGGING,
SET_DRAG_ITEM,
SET_MEDIA_VIEWER_VISIBLE,
SET_PROGRESS_BAR_DATA,
SET_PRIMARY_PROGRESS_BAR_DATA,
SET_SECONDARY_PROGRESS_BAR_DATA,
SET_CONVERT_DIALOG_VISIBLE,
SET_SHARING_PANEL_VISIBLE,
SET_UPDATE_TREE,
@ -50,7 +51,20 @@ const initialState = {
dragging: false,
dragItem: null,
mediaViewerData: { visible: false, id: null },
progressData: { percent: 0, label: "", visible: false },
primaryProgressData: {
percent: 0,
label: "",
visible: false,
icon: "upload",
alert: false,
},
secondaryProgressData: {
percent: 0,
label: "",
visible: false,
icon: "trash",
alert: false,
},
convertDialogVisible: false,
sharingPanelVisible: false,
updateTree: false,
@ -461,9 +475,13 @@ const filesReducer = (state = initialState, action) => {
return Object.assign({}, state, {
mediaViewerData: action.mediaViewerData,
});
case SET_PROGRESS_BAR_DATA:
case SET_PRIMARY_PROGRESS_BAR_DATA:
return Object.assign({}, state, {
progressData: action.progressData,
primaryProgressData: action.primaryProgressData,
});
case SET_SECONDARY_PROGRESS_BAR_DATA:
return Object.assign({}, state, {
secondaryProgressData: action.secondaryProgressData,
});
case SET_CONVERT_DIALOG_VISIBLE:
return Object.assign({}, state, {

View File

@ -965,8 +965,12 @@ export const getConvertDialogVisible = (state) => {
return state.files.convertDialogVisible;
};
export const getProgressData = (state) => {
return state.files.progressData;
export const getPrimaryProgressData = (state) => {
return state.files.primaryProgressData;
};
export const getSecondaryProgressData = (state) => {
return state.files.secondaryProgressData;
};
export const getUpdateTree = (state) => {

View File

@ -0,0 +1,64 @@
import React from "react";
import PropTypes from "prop-types";
import {
StyledFloatingButton,
StyledAlertIcon,
StyledCircleWrap,
StyledCircle,
} from "./StyledFloatingButton";
import { Icons } from "asc-web-components";
const FloatingButton = ({ id, className, style, ...rest }) => {
const { icon, alert, percent } = rest;
return (
<StyledCircleWrap id={id} className={className} style={style} icon={icon}>
<StyledCircle percent={percent}>
<div className="circle__mask circle__full">
<div className="circle__fill"></div>
</div>
<div className="circle__mask">
<div className="circle__fill"></div>
</div>
<StyledFloatingButton>
{icon == "upload" ? (
<Icons.ButtonUploadIcon />
) : icon == "file" ? (
<Icons.ButtonFileIcon />
) : icon == "trash" ? (
<Icons.ButtonTrashIcon />
) : icon == "move" ? (
<Icons.ButtonMoveIcon />
) : (
<Icons.ButtonDuplicateIcon />
)}
<StyledAlertIcon>
{alert ? <Icons.ButtonAlertIcon size="medium" /> : <></>}
</StyledAlertIcon>
</StyledFloatingButton>
</StyledCircle>
</StyledCircleWrap>
);
};
FloatingButton.propTypes = {
id: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object,
icon: PropTypes.oneOf(["upload", "file", "trash", "move", "duplicate"]),
alert: PropTypes.bool,
percent: PropTypes.number,
};
FloatingButton.defaultProps = {
id: undefined,
className: undefined,
style: undefined,
icon: "upload",
alert: false,
percent: 0,
};
export default FloatingButton;

View File

@ -0,0 +1,30 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import {
withKnobs,
select,
boolean,
number,
} from "@storybook/addon-knobs/react";
import Section from "../../../.storybook/decorators/section";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
import FloatingButton from "./index";
storiesOf("Components|Floating Button", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add("floating button", () => {
const iconVariables = ["upload", "file", "trash", "move", "duplicate"];
return (
<Section>
<h1>Floating button</h1>
<FloatingButton
icon={select("icon", iconVariables, "upload")}
alert={boolean("alert", false)}
percent={number("percent", 50)}
/>
</Section>
);
});

View File

@ -0,0 +1,28 @@
# Floating Button
Component that displays floating button
### Usage
```js
import { FloatingButton } from "asc-web-common";
```
```jsx
<FloatingButton icon="upload" alert={false} />
```
```jsx
<FloatingButton icon="trash" alert={true} percent={45} />
```
### Properties
| Props | Type | Required | Values | Default | Description |
| ----------- | :------------: | :------: | :--------------------------------------------: | :------: | --------------------------- |
| `alert` | `bool` | - | - | `false` | Shows the alert |
| `className` | `string` | - | - | - | Accepts class |
| `icon` | `oneOf` | - | `upload`, `file`, `trash`, `move`, `duplicate` | `upload` | Sets the icon on the button |
| `id` | `string` | - | - | - | Accepts id |
| `percent` | `number` | - | - | `0` | Load fullness |
| `style` | `obj`, `array` | - | - | - | Accepts css style |

View File

@ -0,0 +1,76 @@
import styled from "styled-components";
const backgroundColor = "none";
const color = "#2DA7DB";
const StyledCircleWrap = styled.div`
width: 54px;
height: 54px;
background: ${backgroundColor};
border-radius: 50%;
`;
const StyledCircle = styled.div`
.circle__mask,
.circle__fill {
width: 54px;
height: 54px;
position: absolute;
border-radius: 50%;
}
.circle__mask {
clip: rect(0px, 54px, 54px, 27px);
}
.circle__fill {
animation: fill-rotate ease-in-out none;
transform: rotate(${(props) => props.percent * 1.8}deg);
}
.circle__mask .circle__fill {
clip: rect(0px, 27px, 54px, 0px);
background-color: ${color};
}
.circle__mask.circle__full {
animation: fill-rotate ease-in-out none;
transform: rotate(${(props) => props.percent * 1.8}deg);
}
@keyframes fill-rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(${(props) => props.percent * 1.8}deg);
}
}
`;
const StyledFloatingButton = styled.div`
width: 48px;
height: 48px;
border-radius: 50%;
background: #fff;
box-shadow: 0px 5px 20px rgba(0, 0, 0, 0.13);
line-height: 46px;
text-align: center;
margin: 3px;
position: absolute;
`;
const StyledAlertIcon = styled.div`
position: absolute;
width: 12px;
height: 12px;
left: 26px;
top: -10px;
`;
export {
StyledCircleWrap,
StyledCircle,
StyledFloatingButton,
StyledAlertIcon,
};

View File

@ -0,0 +1 @@
export default from "./FloatingButton";

View File

@ -1,7 +1,7 @@
import React, { useEffect } from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { Backdrop, ProgressBar, utils } from "asc-web-components";
import { Backdrop, utils } from "asc-web-components";
import store from "../../store";
import { withTranslation } from "react-i18next";
import i18n from "./i18n";
@ -20,6 +20,7 @@ import SubSectionPaging from "./sub-components/section-paging";
import SectionToggler from "./sub-components/section-toggler";
import { changeLanguage } from "../../utils";
import ReactResizeDetector from "react-resize-detector";
import FloatingButton from "../FloatingButton";
const { getLanguage } = store.auth.selectors;
const { size } = utils.device;
@ -203,11 +204,15 @@ class PageLayoutComponent extends React.Component {
render() {
const {
onDrop,
progressBarDropDownContent,
progressBarLabel,
progressBarValue,
showPrimaryProgressBar,
primaryProgressBarIcon,
primaryProgressBarValue,
showPrimaryButtonAlert,
showSecondaryProgressBar,
secondaryProgressBarValue,
secondaryProgressBarIcon,
showSecondaryButtonAlert,
setSelections,
showProgressBar,
uploadFiles,
viewAs,
withBodyAutoFocus,
@ -382,17 +387,42 @@ class PageLayoutComponent extends React.Component {
</SubSectionPaging>
)}
</SubSectionBody>
{showProgressBar && (
<ProgressBar
className="layout-progress-bar"
label={progressBarLabel}
percent={progressBarValue}
dropDownContent={progressBarDropDownContent}
/>
)}
</>
)}
{showPrimaryProgressBar && showSecondaryProgressBar ? (
<>
<FloatingButton
className="layout-progress-bar"
icon={primaryProgressBarIcon}
percent={primaryProgressBarValue}
alert={showPrimaryButtonAlert}
/>
<FloatingButton
className="layout-progress-second-bar"
icon={secondaryProgressBarIcon}
percent={secondaryProgressBarValue}
alert={showSecondaryButtonAlert}
/>
</>
) : showPrimaryProgressBar && !showSecondaryProgressBar ? (
<FloatingButton
className="layout-progress-bar"
icon={primaryProgressBarIcon}
percent={primaryProgressBarValue}
alert={showPrimaryButtonAlert}
/>
) : !showPrimaryProgressBar && showSecondaryProgressBar ? (
<FloatingButton
className="layout-progress-bar"
icon={secondaryProgressBarIcon}
percent={secondaryProgressBarValue}
alert={showSecondaryButtonAlert}
/>
) : (
<></>
)}
{isArticleAvailable && (
<SectionToggler
visible={!this.state.isArticleVisible}
@ -414,10 +444,15 @@ PageLayoutComponent.propTypes = {
withBodyScroll: PropTypes.bool,
withBodyAutoFocus: PropTypes.bool,
t: PropTypes.func,
showProgressBar: PropTypes.bool,
progressBarValue: PropTypes.number,
showPrimaryProgressBar: PropTypes.bool,
primaryProgressBarValue: PropTypes.number,
showPrimaryButtonAlert: PropTypes.bool,
progressBarDropDownContent: PropTypes.any,
progressBarLabel: PropTypes.string,
primaryProgressBarIcon: PropTypes.string,
showSecondaryProgressBar: PropTypes.bool,
secondaryProgressBarValue: PropTypes.number,
secondaryProgressBarIcon: PropTypes.string,
showSecondaryButtonAlert: PropTypes.bool,
onDrop: PropTypes.func,
setSelections: PropTypes.func,
uploadFiles: PropTypes.bool,

View File

@ -21,12 +21,22 @@ const StyledSection = styled.section`
flex-direction: column;
/*width: ${(props) => `${props.widthProp}px`};*/
.layout-progress-bar {
bottom: 0;
position: sticky;
margin-left: -24px;
position: fixed;
right: 15px;
bottom: 21px;
@media ${tablet} {
margin-left: -16px;
bottom: 83px;
}
}
.layout-progress-second-bar {
position: fixed;
right: 15px;
bottom: 83px;
@media ${tablet} {
bottom: 145px;
}
}

View File

@ -0,0 +1,4 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="6" cy="6" r="6" fill="#F21C0E"/>
<path d="M6 9.93747C5.48319 9.93747 5.0625 9.51678 5.0625 8.99997C5.0625 8.48316 5.48319 8.06247 6 8.06247C6.51681 8.06247 6.9375 8.48316 6.9375 8.99997C6.9375 9.51678 6.51681 9.93747 6 9.93747ZM6.9375 6.06253C6.9375 6.57934 6.51682 6.99999 6 6.99999C5.48319 6.99999 5.0625 6.57934 5.0625 6.06253L5.0625 3.00002C5.0625 2.48321 5.48319 2.0625 6 2.0625C6.51681 2.0625 6.9375 2.48321 6.9375 3.00002L6.9375 6.06253Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 576 B

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.46129 22L14.5387 22C15.3445 22 16 21.3445 16 20.5388L16 16.9046L9.59254 16.9046C8.05121 16.9046 7.0996 15.8556 7.0996 14.3144L7.0996 8L3.46128 8C2.65553 8 1.99999 8.65549 1.99999 9.4612L1.99999 20.5388C2.00003 21.3445 2.65553 22 3.46129 22Z" fill="#A3A9AE"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.43787 16L20.5622 16C21.3549 16 22 15.3551 22 14.5623L22 3.43767C21.9999 2.64493 21.355 2 20.5621 2L9.43782 2C8.64499 2 7.99998 2.64493 7.99998 3.43762L7.99999 14.5623C7.99994 15.3551 8.64503 16 9.43787 16ZM15 13C14.4477 13 14 12.5523 14 12L14 10.5C14 10.2239 13.7761 10 13.5 10L12 10C11.4477 10 11 9.55229 11 9C11 8.44772 11.4477 8 12 8L13.5 8C13.7761 8 14 7.77614 14 7.5L14 6C14 5.44772 14.4477 5 15 5C15.5523 5 16 5.44772 16 6L16 7.5C16 7.77614 16.2238 8 16.5 8L18 8C18.5523 8 19 8.44772 19 9C19 9.55228 18.5523 10 18 10L16.5 10C16.2238 10 16 10.2239 16 10.5L16 12C16 12.5523 15.5523 13 15 13Z" fill="#A3A9AE"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 2C4.44772 2 4 2.44772 4 3L4 21C4 21.5523 4.44772 22 5 22L19 22C19.5523 22 20 21.5523 20 21L20 7L15.1111 2H5ZM9 19C9.55228 19 10 18.5523 10 18C10 17.4477 9.55228 17 9 17C8.44771 17 8 17.4477 8 18C8 18.5523 8.44771 19 9 19ZM13 18C13 18.5523 12.5523 19 12 19C11.4477 19 11 18.5523 11 18C11 17.4477 11.4477 17 12 17C12.5523 17 13 17.4477 13 18ZM15 19C15.5523 19 16 18.5523 16 18C16 17.4477 15.5523 17 15 17C14.4477 17 14 17.4477 14 18C14 18.5523 14.4477 19 15 19Z" fill="#A3A9AE"/>
</svg>

After

Width:  |  Height:  |  Size: 633 B

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.99854 2H8.99885L11.9999 4.99353H20.9966C21.5488 4.99353 21.9966 5.44125 21.9966 5.99353V20C21.9966 20.5523 21.5488 21 20.9966 21H3.05905C2.50815 21 2.06101 20.5545 2.05905 20.0036L1.99854 3.00356C1.99657 2.44989 2.44486 2 2.99854 2ZM11.0003 9.1743C11.0003 8.31994 12.0024 7.85903 12.6511 8.41504L17.1145 12.2408C17.5801 12.6399 17.5801 13.3602 17.1145 13.7593L12.6511 17.5851C12.0024 18.1411 11.0003 17.6802 11.0003 16.8259V16.0005C11.0003 15.4482 10.5526 15.0005 10.0003 15.0005H7.00031C6.44802 15.0005 6.00031 14.5528 6.00031 14.0005V12.0005C6.00031 11.4482 6.44802 11.0005 7.00031 11.0005H10.0003C10.5526 11.0005 11.0003 10.5528 11.0003 10.0005V9.1743Z" fill="#A3A9AE"/>
</svg>

After

Width:  |  Height:  |  Size: 829 B

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.25 5C2.25 4.44772 2.69772 4 3.25 4H7L7.00959 1.99522C7.01222 1.44481 7.45916 1 8.00958 1H15C15.5523 1 16 1.44772 16 2V4H19.5C20.0523 4 20.5 4.44772 20.5 5V6C20.5 6.55228 20.0523 7 19.5 7H3.25C2.69772 7 2.25 6.55228 2.25 6V5ZM4 8H4.03965H18.9604C18.9802 8 19 20.843 19 20.843C19 21.4793 18.4648 22 17.8106 22H5.18944C4.53525 22 4 21.4793 4 20.843V8ZM6.5 10C6.22386 10 6 10.2239 6 10.5V19.5C6 19.7761 6.22386 20 6.5 20C6.77614 20 7 19.7761 7 19.5V10.5C7 10.2239 6.77614 10 6.5 10ZM11 10.5C11 10.2239 11.2239 10 11.5 10C11.7761 10 12 10.2239 12 10.5V19.5C12 19.7761 11.7761 20 11.5 20C11.2239 20 11 19.7761 11 19.5V10.5ZM16.5 10C16.2239 10 16 10.2239 16 10.5V19.5C16 19.7761 16.2239 20 16.5 20C16.7761 20 17 19.7761 17 19.5V10.5C17 10.2239 16.7761 10 16.5 10ZM14 3H9V4H14V3Z" fill="#A3A9AE"/>
</svg>

After

Width:  |  Height:  |  Size: 945 B

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.19048 18.9959V19H19.0476V18.9965C21.7997 18.8953 24 16.6109 24 13.8077C24 10.9401 21.6975 8.61538 18.8571 8.61538H18.5923C17.7367 5.89494 16 3 11.619 3C7 3 4.77093 6.68754 4.5 10.1538H4.38095C1.96142 10.1538 0 12.1341 0 14.5769C0 16.9553 1.85931 18.8952 4.19048 18.9959ZM10.009 19L14.0092 19L14.0092 16.5C14.0092 16.2239 14.2331 16 14.5092 16H15.8258C16.6801 16 17.141 14.9979 16.585 14.3492L12.7593 9.8858C12.3602 9.42019 11.6398 9.42019 11.2407 9.8858L7.41496 14.3492C6.85896 14.9979 7.31987 16 8.17422 16H9.50902C9.78516 16 10.009 16.2239 10.009 16.5L10.009 19Z" fill="#A3A9AE"/>
</svg>

After

Width:  |  Height:  |  Size: 738 B

View File

@ -200,6 +200,13 @@ import OrigMoveToIcon from "./move.react.svg";
import OrigIntegrationIcon from "./integration.react.svg";
import OrigButtonUploadIcon from "./button.upload.react.svg";
import OrigButtonFileIcon from "./button.file.react.svg";
import OrigButtonTrashIcon from "./button.trash.react.svg";
import OrigButtonMoveIcon from "./button.move.react.svg";
import OrigButtonDuplicateIcon from "./button.duplicate.react.svg";
import OrigButtonAlertIcon from "./button.alert.react.svg";
export const AZSortingIcon = createStyledIcon(
OrigAZSortingIcon,
"AZSortingIcon"
@ -265,6 +272,30 @@ export const ArrowContentIcon = createStyledIcon(
"ArrowContentIcon"
);
export const BloggerIcon = createStyledIcon(OrigBloggerIcon, "BloggerIcon");
export const ButtonUploadIcon = createStyledIcon(
OrigButtonUploadIcon,
"ButtonUploadIcon"
);
export const ButtonFileIcon = createStyledIcon(
OrigButtonFileIcon,
"ButtonFileIcon"
);
export const ButtonTrashIcon = createStyledIcon(
OrigButtonTrashIcon,
"ButtonTrashIcon"
);
export const ButtonMoveIcon = createStyledIcon(
OrigButtonMoveIcon,
"ButtonMoveIcon"
);
export const ButtonDuplicateIcon = createStyledIcon(
OrigButtonDuplicateIcon,
"ButtonDuplicateIcon"
);
export const ButtonAlertIcon = createStyledIcon(
OrigButtonAlertIcon,
"ButtonAlertIcon"
);
export const CalendarCheckedIcon = createStyledIcon(
OrigCalendarCheckedIcon,
"CalendarCheckedIcon"