diff --git a/products/ASC.Files/Client/src/components/Article/Body/TreeFolders.js b/products/ASC.Files/Client/src/components/Article/Body/TreeFolders.js index 9d7028035b..c733ad8d6a 100644 --- a/products/ASC.Files/Client/src/components/Article/Body/TreeFolders.js +++ b/products/ASC.Files/Client/src/components/Article/Body/TreeFolders.js @@ -1,7 +1,5 @@ import React from "react"; import { TreeMenu, TreeNode, Icons, toastr, utils } from "asc-web-components"; -import { fetchFiles } from "../../../store/files/actions"; -import store from "../../../store/store"; import { api } from "asc-web-common"; const { files } = api; @@ -10,8 +8,7 @@ class TreeFolders extends React.Component { super(props); const treeData = props.data; - - this.state = { treeData, expandedKeys: this.props.expandedKeys }; + this.state = { treeData, expandedKeys: props.expandedKeys }; this.ref = React.createRef(); } @@ -56,18 +53,6 @@ class TreeFolders extends React.Component { } }; - onSelect = data => { - if (this.props.selectedKeys[0] !== data[0]) { - this.props.onLoading(true); - const newFilter = this.props.filter.clone(); - fetchFiles(data[0], newFilter, store.dispatch).catch(err => - toastr.error("Something went wrong", err) - ).finally(() => this.props.onLoading(false)); - } - - //this.props.selectFolder(data && data.length === 1 && data[0] !== "root" ? data[0] : null); - }; - loop = (data, curId, child, level) => { //if (level < 1 || curId.length - 3 > level * 2) return; data.forEach(item => { @@ -156,7 +141,7 @@ class TreeFolders extends React.Component { const treeData = [...this.state.treeData]; this.getNewTreeData(treeData, listIds, data.folders, 10); - this.props.setTreeFolders(treeData); + this.props.needUpdate && this.props.setTreeFolders(treeData); this.setState({ treeData }); }) .catch(() => this.props.onLoading(false)) @@ -164,16 +149,18 @@ class TreeFolders extends React.Component { }; onExpand = data => { - const newFilter = this.props.filter; - newFilter.treeFolders = data; + if(this.props.needUpdate) { + const newFilter = this.props.filter.clone(); + newFilter.treeFolders = data; + this.props.setFilter(newFilter); + } - this.props.setFilter(newFilter); this.setState({ expandedKeys: data }); }; componentDidUpdate(prevProps) { - const { expandedKeys, data } = this.props; - if (this.state.expandedKeys.length !== expandedKeys.length) { + const { expandedKeys, data, needUpdate } = this.props; + if (needUpdate && expandedKeys && this.state.expandedKeys.length !== expandedKeys.length) { this.setState({ expandedKeys }); } @@ -183,7 +170,7 @@ class TreeFolders extends React.Component { } render() { - const { selectedKeys, fakeNewDocuments, isLoading } = this.props; + const { selectedKeys, fakeNewDocuments, isLoading, onSelect } = this.props; const { treeData, expandedKeys } = this.state; return ( @@ -196,7 +183,7 @@ class TreeFolders extends React.Component { multiple={false} showIcon switcherIcon={this.switcherIcon} - onSelect={this.onSelect} + onSelect={onSelect} selectedKeys={selectedKeys} badgeLabel={fakeNewDocuments} onBadgeClick={() => console.log("onBadgeClick")} @@ -210,4 +197,9 @@ class TreeFolders extends React.Component { } } +TreeFolders.defaultProps = { + selectedKeys: [], + needUpdate: true +}; + export default TreeFolders; diff --git a/products/ASC.Files/Client/src/components/Article/Body/index.js b/products/ASC.Files/Client/src/components/Article/Body/index.js index e0d58347b9..8a89950240 100644 --- a/products/ASC.Files/Client/src/components/Article/Body/index.js +++ b/products/ASC.Files/Client/src/components/Article/Body/index.js @@ -29,10 +29,10 @@ class ArticleBodyContent extends React.Component { expandedKeys.pop(); fetchFiles(folderId, newFilter, store.dispatch).catch(err => - toastr.error("Something went wrong", err) + toastr.error(err) ); }) - .catch(err => toastr.error("Something went wrong", err)) + .catch(err => toastr.error(err)) .finally(() => this.setState({ expandedKeys })); } @@ -46,12 +46,22 @@ class ArticleBodyContent extends React.Component { } } + onSelect = data => { + const { selectedKeys, filter, onLoading } = this.props; + if (selectedKeys[0] !== data[0]) { + onLoading(true); + const newFilter = filter.clone(); + fetchFiles(data[0], newFilter, store.dispatch).catch(err => + toastr.error(err) + ).finally(() => onLoading(false)); + } + }; + render() { const { data, selectedKeys, fakeNewDocuments, - currentModule, filter, setFilter, setTreeFolders, @@ -64,7 +74,7 @@ class ArticleBodyContent extends React.Component { { - const { startUploadSession, closeUploadSession, onDownloadProgress, onClose } = this.props; + const { startUploadSession, closeUploadSession, onDownloadProgress, onClose, t } = this.props; const downloadItems = this.getDownloadItems(); const fileConvertIds = downloadItems[0]; const folderIds = downloadItems[1]; - startUploadSession(); + startUploadSession(t("ArchivingData")); api.files .downloadFormatFiles(fileConvertIds, folderIds) diff --git a/products/ASC.Files/Client/src/components/dialogs/DownloadDialog/locales/en/translation.json b/products/ASC.Files/Client/src/components/dialogs/DownloadDialog/locales/en/translation.json index b09a608b7c..f120185924 100644 --- a/products/ASC.Files/Client/src/components/dialogs/DownloadDialog/locales/en/translation.json +++ b/products/ASC.Files/Client/src/components/dialogs/DownloadDialog/locales/en/translation.json @@ -11,5 +11,6 @@ "ConvertToZip": "Files will be compressed into the .zip file", "ConvertMessage": "If you choose to convert the file to the format different from the original, some data might be lost.", "DownloadButton": "Download", - "CancelButton": "Cancel" + "CancelButton": "Cancel", + "ArchivingData": "Archiving data" } \ No newline at end of file diff --git a/products/ASC.Files/Client/src/components/dialogs/DownloadDialog/locales/ru/translation.json b/products/ASC.Files/Client/src/components/dialogs/DownloadDialog/locales/ru/translation.json index 3f37c52dd2..86d3ab1378 100644 --- a/products/ASC.Files/Client/src/components/dialogs/DownloadDialog/locales/ru/translation.json +++ b/products/ASC.Files/Client/src/components/dialogs/DownloadDialog/locales/ru/translation.json @@ -11,5 +11,6 @@ "ConvertToZip": "Файлы будут упакованы в .zip файл", "ConvertMessage": "Если вы решите сконвертировать файл в формат, отличный от исходного, некоторые данные могут быть потеряны.", "DownloadButton": "Скачать", - "CancelButton": "Отмена" + "CancelButton": "Отмена", + "ArchivingData": "Архивирование данных" } \ No newline at end of file diff --git a/products/ASC.Files/Client/src/components/pages/Home/Section/Header/index.js b/products/ASC.Files/Client/src/components/pages/Home/Section/Header/index.js index bb7fea8168..f15d0c217b 100644 --- a/products/ASC.Files/Client/src/components/pages/Home/Section/Header/index.js +++ b/products/ASC.Files/Client/src/components/pages/Home/Section/Header/index.js @@ -14,7 +14,7 @@ import { import { fetchFiles, setAction } from "../../../../../store/files/actions"; import { default as filesStore } from "../../../../../store/store"; import { EmptyTrashDialog, DeleteDialog, DownloadDialog } from "../../../../dialogs"; -import { SharingPanel } from "../../../../panels"; +import { SharingPanel, OperationsPanel } from "../../../../panels"; import { isCanBeDeleted, checkFolderType } from "../../../../../store/files/selectors"; const { isAdmin } = store.auth.selectors; @@ -103,7 +103,9 @@ class SectionHeaderContent extends React.Component { showSharingPanel: false, showDeleteDialog: false, showDownloadDialog: false, - showEmptyTrashDialog: false + showEmptyTrashDialog: false, + showMoveToPanel: false, + showCopyPanel: false }; } @@ -162,14 +164,14 @@ class SectionHeaderContent extends React.Component { createLinkForPortalUsers = () => toastr.info("createLinkForPortalUsers click"); - moveAction = () => toastr.info("moveAction click"); + onMoveAction = () => this.setState({ showMoveToPanel: !this.state.showMoveToPanel }); - copyAction = () => toastr.info("copyAction click"); + onCopyAction = () => this.setState({ showCopyPanel: !this.state.showCopyPanel }); - startUploadSession = () => { - const { onLoading, t, setProgressLabel, setProgressVisible} = this.props; + startFilesOperations = progressBarLabel => { + const { onLoading, setProgressLabel, setProgressVisible} = this.props; onLoading(true); - setProgressLabel(t("ArchivingData")); + setProgressLabel(progressBarLabel); setProgressVisible(true); } @@ -207,7 +209,7 @@ class SectionHeaderContent extends React.Component { } } - this.startUploadSession(); + this.startFilesOperations(this.props.t("ArchivingData")); api.files .downloadFiles(fileIds, folderIds) @@ -249,13 +251,13 @@ class SectionHeaderContent extends React.Component { { key: "move-to", label: t("MoveTo"), - onClick: this.moveAction, + onClick: this.onMoveAction, disabled: true }, { key: "copy", label: t("Copy"), - onClick: this.copyAction, + onClick: this.onCopyAction, disabled: true }, { @@ -287,8 +289,6 @@ class SectionHeaderContent extends React.Component { ); }; - - render() { //console.log("Body header render"); @@ -306,13 +306,18 @@ class SectionHeaderContent extends React.Component { onCheck, title, currentFolderId, - onLoading + onLoading, + isLoading, + filter, + setProgressValue } = this.props; const { showDeleteDialog, showSharingPanel, showEmptyTrashDialog, - showDownloadDialog + showDownloadDialog, + showMoveToPanel, + showCopyPanel } = this.state; const isItemsSelected = selection.length; const isOnlyFolderSelected = selection.every( @@ -369,12 +374,12 @@ class SectionHeaderContent extends React.Component { { label: t("MoveTo"), disabled: !isItemsSelected, - onClick: this.moveAction + onClick: this.onMoveAction }, { label: t("Copy"), disabled: !isItemsSelected, - onClick: this.copyAction + onClick: this.onCopyAction }, { label: t("Delete"), @@ -389,6 +394,14 @@ class SectionHeaderContent extends React.Component { onClick: this.onEmptyTrashAction }); + const operationsPanelProps = { + onLoading, + isLoading, + setProgressValue, + startFilesOperations: this.startFilesOperations, + closeUploadSession: this.closeUploadSession + }; + return ( {isHeaderVisible ? ( @@ -486,11 +499,29 @@ class SectionHeaderContent extends React.Component { /> )} + {showMoveToPanel && ( + + )} + + {showCopyPanel && ( + + )} + {showDownloadDialog && ( diff --git a/products/ASC.Files/Client/src/components/pages/Home/index.js b/products/ASC.Files/Client/src/components/pages/Home/index.js index 0584baadd3..7e7c757354 100644 --- a/products/ASC.Files/Client/src/components/pages/Home/index.js +++ b/products/ASC.Files/Client/src/components/pages/Home/index.js @@ -151,7 +151,7 @@ class PureHome extends React.Component { return ( <> { + const { + getProgress, + setProgressValue, + closeUploadSession, + filter, + currentFolderId, + treeFolders, + getFolder + } = this.props; + getProgress().then(res => { + const currentItem = res.find(x => x.id === id); + if(currentItem && currentItem.progress !== 100) { + setProgressValue(currentItem.progress); + setTimeout(() => this.loop(id, destFolderId), 1000); + } else { + getFolder(destFolderId).then(data => { + let newTreeFolders = treeFolders; + let path = data.pathParts.slice(0); + let folders = data.folders; + let foldersCount = data.current.foldersCount; + loopTreeFolders(path, newTreeFolders, folders, foldersCount); + + fetchFiles(currentFolderId, filter, filesStore.dispatch).then((data) => { + newTreeFolders = treeFolders; + path = data.selectedFolder.pathParts.slice(0); + folders = data.selectedFolder.folders; + foldersCount = data.selectedFolder.foldersCount; + loopTreeFolders(path, newTreeFolders, folders, foldersCount); + setTreeFolders(newTreeFolders); + }).catch(err => closeUploadSession(err)) + .finally(() => { + setProgressValue(100); + closeUploadSession(); + }) + }).catch(err => closeUploadSession(err)) + } + }).catch(err => closeUploadSession(err)); + } + + onSelect = e => { + const { + t, + isCopy, + onClose, + selection, + startFilesOperations, + closeUploadSession, + copyToFolder, + moveToFolder + } = this.props; + + const destFolderId = Number(e); + const conflictResolveType = "skip"; //Skip, Overwrite, Duplicate + const deleteAfter = true; + const folderIds = []; + const fileIds = []; + + for(let item of selection) { + if(item.fileExst) { + fileIds.push(item.id); + } else { + folderIds.push(item.id); + } + } + + onClose(); + + if(isCopy) { + startFilesOperations(t("CopyOperation")); + copyToFolder(destFolderId, folderIds, fileIds, conflictResolveType, deleteAfter) + .then(res => this.loop(res[0].id, destFolderId)) + .catch(err => closeUploadSession(err)) + } else { + startFilesOperations(t("MoveToOperation")); + moveToFolder(destFolderId, folderIds, fileIds, conflictResolveType, deleteAfter) + .then(res => this.loop(res[0].id, destFolderId)) + .catch(err => closeUploadSession(err)) + } + } + + render() { + //console.log("Operations panel render"); + const { t, visible, onClose, onLoading, isLoading, filter, treeFolders, isCopy } = this.props; + const zIndex = 310; + const fakeNewDocuments = 8; + const data = treeFolders.slice(0, 3); + const expandedKeys = this.props.expandedKeys.map(item => item.toString()); + + return ( + + + + + ); + } +} + +OperationsPanelComponent.propTypes = { + onClose: PropTypes.func, + visible: PropTypes.bool, +}; + +const OperationsPanelContainerTranslated = withTranslation()(OperationsPanelComponent); + +const OperationsPanel = (props) => ( + +); + +const mapStateToProps = (state) => { + + const { selectedFolder, selection, treeFolders, filter } = state.files; + const { pathParts, id } = selectedFolder; + + return { + treeFolders, + filter, + selection, + expandedKeys: pathParts, + currentFolderId: id + }; +}; + +export default connect(mapStateToProps, { + setTreeFolders, + getFolder, + getProgress, + copyToFolder, + moveToFolder, +})(withRouter(OperationsPanel)); diff --git a/products/ASC.Files/Client/src/components/panels/OperationsPanel/locales/en/translation.json b/products/ASC.Files/Client/src/components/panels/OperationsPanel/locales/en/translation.json new file mode 100644 index 0000000000..2c7629dcd0 --- /dev/null +++ b/products/ASC.Files/Client/src/components/panels/OperationsPanel/locales/en/translation.json @@ -0,0 +1,6 @@ +{ + "Copy": "Copy", + "Move": "Move", + "CopyOperation": "Copying", + "MoveToOperation": "Moving" +} \ No newline at end of file diff --git a/products/ASC.Files/Client/src/components/panels/OperationsPanel/locales/ru/translation.json b/products/ASC.Files/Client/src/components/panels/OperationsPanel/locales/ru/translation.json new file mode 100644 index 0000000000..6df5adc93b --- /dev/null +++ b/products/ASC.Files/Client/src/components/panels/OperationsPanel/locales/ru/translation.json @@ -0,0 +1,6 @@ +{ + "Copy": "Копирование", + "Move": "Перемещение", + "CopyOperation": "Копирование", + "MoveToOperation": "Перемещение" +} \ No newline at end of file diff --git a/products/ASC.Files/Client/src/components/panels/SharingPanel/SharingPanel.js b/products/ASC.Files/Client/src/components/panels/SharingPanel/SharingPanel.js index e5e9efd8c7..f46434462f 100644 --- a/products/ASC.Files/Client/src/components/panels/SharingPanel/SharingPanel.js +++ b/products/ASC.Files/Client/src/components/panels/SharingPanel/SharingPanel.js @@ -9,7 +9,6 @@ import { Button, DropDown, DropDownItem, - utils, toastr, Textarea, ComboBox, @@ -18,12 +17,12 @@ import { import { connect } from "react-redux"; import { withRouter } from "react-router"; import { withTranslation } from "react-i18next"; -import { utils as commonUtils, constants, api } from "asc-web-common"; +import { utils as commonUtils, constants } from "asc-web-common"; import i18n from "./i18n"; import { getShareUsers, setShareFiles } from "../../../store/files/actions"; import { getAccessOption } from '../../../store/files/selectors'; import { - StyledSharingPanel, + StyledAsidePanel, StyledContent, StyledFooter, StyledSharingHeaderContent, @@ -34,7 +33,6 @@ import SharingRow from "./SharingRow"; const { changeLanguage } = commonUtils; const { ShareAccessRights } = constants; -const { files } = api; class SharingPanelComponent extends React.Component { constructor(props) { @@ -561,7 +559,7 @@ class SharingPanelComponent extends React.Component { ); return ( - +