From c860c031d584b70ea851a81ff6136e157e9a469e Mon Sep 17 00:00:00 2001 From: zhouwenxuan Date: Fri, 15 Nov 2024 15:41:19 +0800 Subject: [PATCH] update copy dirent dialog --- .../components/dialog/copy-dirent-dialog.js | 280 ++++++++++++++---- .../components/dialog/move-dirent-dialog.js | 26 +- .../components/dialog/select-dirent-body.js | 2 +- .../dirent-grid-view/dirent-grid-view.js | 1 + .../dirent-list-view/dirent-list-item.js | 1 + .../dirent-list-view/dirent-list-view.js | 1 + frontend/src/components/file-chooser/index.js | 11 +- .../file-chooser/repo-list-wrapper.js | 2 +- .../components/file-chooser/searcher/index.js | 2 +- .../toolbar/selected-dirents-toolbar.js | 1 + frontend/src/constants/index.js | 9 + .../lib-content-view/lib-content-view.js | 20 +- 12 files changed, 275 insertions(+), 81 deletions(-) diff --git a/frontend/src/components/dialog/copy-dirent-dialog.js b/frontend/src/components/dialog/copy-dirent-dialog.js index 9fbbe8c579e..8d7e1754273 100644 --- a/frontend/src/components/dialog/copy-dirent-dialog.js +++ b/frontend/src/components/dialog/copy-dirent-dialog.js @@ -1,9 +1,15 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Button, Modal, ModalHeader, ModalFooter, ModalBody, Alert, Row, Col } from 'reactstrap'; -import FileChooser from '../file-chooser'; -import { gettext } from '../../utils/constants'; +import { Modal, ModalHeader } from 'reactstrap'; +import { IconBtn } from '@seafile/sf-metadata-ui-component'; +import Searcher from '../file-chooser/searcher'; +import SelectDirentBody from './select-dirent-body'; +import { MODE_TYPE_MAP } from '../../constants'; import { Utils } from '../../utils/utils'; +import { seafileAPI } from '../../utils/seafile-api'; +import { gettext, isPro } from '../../utils/constants'; +import { RepoInfo } from '../../models'; +import toaster from '../toast'; const propTypes = { path: PropTypes.string.isRequired, @@ -15,21 +21,70 @@ const propTypes = { onItemsCopy: PropTypes.func, onCancelCopy: PropTypes.func.isRequired, repoEncrypted: PropTypes.bool.isRequired, + onAddFolder: PropTypes.func, }; -// need dirent file Path; class CopyDirent extends React.Component { constructor(props) { super(props); this.state = { - repo: { repo_id: this.props.repoID }, + mode: MODE_TYPE_MAP.ONLY_CURRENT_LIBRARY, + currentRepo: { repo_id: this.props.repoID }, + selectedRepo: { repo_id: this.props.repoID }, + repoList: [], selectedPath: this.props.path, + selectedSearchedRepo: null, + selectedSearchedItem: { repoID: '', filePath: '' }, + searchStatus: '', + searchResults: [], + showSearchBar: false, errMessage: '', - mode: 'only_current_library', + initToShowChildren: false, }; + this.lastMode = MODE_TYPE_MAP.ONLY_CURRENT_LIBRARY; } + componentDidMount() { + this.initialize(); + } + + initialize = async () => { + try { + const res = await seafileAPI.getRepoInfo(this.props.repoID); + const repo = new RepoInfo(res.data); + this.setState({ currentRepo: repo }); + await this.fetchRepoList(); + } catch (error) { + const errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); + } + }; + + fetchRepoList = async () => { + try { + const res = await seafileAPI.listRepos(); + const repos = res.data.repos; + const repoList = []; + const uniqueRepoIds = new Set(); + for (const repo of repos) { + if (repo.permission === 'rw' && repo.repo_id !== this.props.repoID && !uniqueRepoIds.has(repo.repo_id)) { + uniqueRepoIds.add(repo.repo_id); + repoList.push(repo); + } + } + const sortedRepoList = Utils.sortRepos(repoList, 'name', 'asc'); + const selectedRepo = sortedRepoList.find((repo) => repo.repo_id === this.props.repoID); + this.setState({ + repoList: sortedRepoList, + repo: selectedRepo, + }); + } catch (error) { + const errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); + } + }; + handleSubmit = () => { if (this.props.isMultipleOperation) { this.copyItems(); @@ -39,10 +94,10 @@ class CopyDirent extends React.Component { }; copyItems = () => { - let { repo, selectedPath } = this.state; + let { selectedRepo, selectedPath } = this.state; let message = gettext('Invalid destination path'); - if (!repo || selectedPath === '') { + if (!selectedRepo || selectedPath === '') { this.setState({ errMessage: message }); return; } @@ -78,16 +133,16 @@ class CopyDirent extends React.Component { return; } - this.props.onItemsCopy(repo, selectedPath); + this.props.onItemsCopy(selectedRepo, selectedPath, true); this.toggle(); }; copyItem = () => { - let { repo, repoID, selectedPath } = this.state; + let { repoID, selectedRepo, selectedPath } = this.state; let direntPath = Utils.joinPath(this.props.path, this.props.dirent.name); let message = gettext('Invalid destination path'); - if (!repo || (repo.repo_id === repoID && selectedPath === '')) { + if (!selectedRepo || (selectedRepo.repo_id === repoID && selectedPath === '')) { this.setState({ errMessage: message }); return; } @@ -107,7 +162,7 @@ class CopyDirent extends React.Component { return; } - this.props.onItemCopy(repo, this.props.dirent, selectedPath, this.props.path); + this.props.onItemCopy(selectedRepo, this.props.dirent, selectedPath, this.props.path, true); this.toggle(); }; @@ -115,6 +170,113 @@ class CopyDirent extends React.Component { this.props.onCancelCopy(); }; + selectRepo = (repo) => { + this.setState({ selectedRepo: repo }); + }; + + selectSearchedRepo = (repo) => { + this.setState({ selectedSearchedRepo: repo }); + }; + + setSelectedPath = (selectedPath) => { + this.setState({ selectedPath }); + }; + + setErrMessage = (message) => { + this.setState({ errMessage: message }); + }; + + updateMode = (mode) => { + if (mode === this.state.mode) return; + + if (mode !== MODE_TYPE_MAP.SEARCH_RESULTS) { + this.lastMode = mode; + } + + const isShowChildren = mode === MODE_TYPE_MAP.ONLY_CURRENT_LIBRARY || mode === MODE_TYPE_MAP.SEARCH_RESULTS; + this.setState({ + mode, + initToShowChildren: isShowChildren, + }); + + if (this.state.mode === MODE_TYPE_MAP.SEARCH_RESULTS) { + this.setState({ + selectedSearchedRepo: null, + searchResults: [], + showSearchBar: false, + }); + } + + if (this.state.selectedSearchedRepo && mode !== MODE_TYPE_MAP.SEARCH_RESULTS) { + this.setState({ + selectedSearchedRepo: null, + searchResults: [], + showSearchBar: false, + }); + } + + this.setState({ selectedSearchedItem: { repoID: '', filePath: '' } }); + }; + + onUpdateSearchStatus = (status) => { + this.setState({ searchStatus: status }); + }; + + onUpdateSearchResults = (results) => { + this.setState({ + searchResults: results + }); + }; + + onOpenSearchBar = () => { + this.setState({ showSearchBar: true }); + }; + + onCloseSearchBar = () => { + const mode = this.lastMode; + this.setState({ + mode, + searchStatus: '', + searchResults: [], + selectedSearchedRepo: null, + showSearchBar: false, + initToShowChildren: mode === MODE_TYPE_MAP.ONLY_CURRENT_LIBRARY, + }); + }; + + onSearchedItemClick = (item) => { + item['type'] = item.is_dir ? 'dir' : 'file'; + let repo = new RepoInfo(item); + this.onDirentItemClick(repo, item.path, item); + }; + + onSearchedItemDoubleClick = (item) => { + if (item.type !== 'dir') return; + + seafileAPI.getRepoInfo(item.repo_id).then(res => { + const repoInfo = new RepoInfo(res.data); + const path = item.path.substring(0, item.path.length - 1); + const mode = item.repo_id === this.props.repoID ? MODE_TYPE_MAP.ONLY_CURRENT_LIBRARY : MODE_TYPE_MAP.ONLY_OTHER_LIBRARIES; + this.lastMode = mode; + this.setState({ + mode, + selectedRepo: repoInfo, + selectedSearchedRepo: repoInfo, + selectedPath: path, + selectedSearchedItem: { repoID: item.repo_id, filePath: path }, + showSearchBar: mode === MODE_TYPE_MAP.ONLY_OTHER_LIBRARIES, + initToShowChildren: true, + }); + }).catch(err => { + const errMessage = Utils.getErrorMsg(err); + toaster.danger(errMessage); + }); + }; + + selectSearchedItem = (item) => { + this.setState({ selectedSearchedItem: item }); + }; + onDirentItemClick = (repo, selectedPath) => { this.setState({ repo: repo, @@ -131,10 +293,6 @@ class CopyDirent extends React.Component { }); }; - onSelectedMode = (mode) => { - this.setState({ mode: mode }); - }; - renderTitle = () => { const { dirent, isMultipleOperation } = this.props; let title = gettext('Copy {placeholder} to'); @@ -147,48 +305,68 @@ class CopyDirent extends React.Component { }; render() { - const { dirent, selectedDirentList, isMultipleOperation, repoID, path } = this.props; - const { mode, errMessage } = this.state; + const { dirent, selectedDirentList, isMultipleOperation, path } = this.props; + const { mode, currentRepo, selectedRepo, selectedPath, showSearchBar, searchStatus, searchResults, selectedSearchedRepo } = this.state; const copiedDirent = dirent || selectedDirentList[0]; const { permission } = copiedDirent; const { isCustomPermission } = Utils.getUserPermission(permission); - const LibraryOption = ({ mode, label }) => ( -
this.onSelectedMode(mode)}> - {label} -
- ); - return ( - - - {isMultipleOperation ? this.renderTitle() :
} -
- - - - {!isCustomPermission && } - - - - - + + + + }> + {isMultipleOperation ? this.renderTitle() :
} + {isPro && ( + showSearchBar ? ( + + ) : ( + {}} + tabIndex={0} /> - {errMessage && {errMessage}} -
- - - - - -
+ ) + )} + +
); } diff --git a/frontend/src/components/dialog/move-dirent-dialog.js b/frontend/src/components/dialog/move-dirent-dialog.js index 1e4a2231000..150480da03e 100644 --- a/frontend/src/components/dialog/move-dirent-dialog.js +++ b/frontend/src/components/dialog/move-dirent-dialog.js @@ -9,15 +9,7 @@ import Searcher from '../file-chooser/searcher'; import { RepoInfo } from '../../models'; import { seafileAPI } from '../../utils/seafile-api'; import toaster from '../toast'; - -export const MODE_TYPE_MAP = { - CURRENT_AND_OTHER_REPOS: 'current_repo_and_other_repos', - ONLY_CURRENT_LIBRARY: 'only_current_library', - ONLY_ALL_REPOS: 'only_all_repos', - ONLY_OTHER_LIBRARIES: 'only_other_libraries', - RECENTLY_USED: 'recently_used', - SEARCH_RESULTS: 'search_results', -}; +import { MODE_TYPE_MAP } from '../../constants'; const propTypes = { path: PropTypes.string.isRequired, @@ -53,12 +45,20 @@ class MoveDirent extends React.Component { } componentDidMount() { - seafileAPI.getRepoInfo(this.props.repoID).then(res => { + this.initialize(); + } + + initialize = async () => { + try { + const res = await seafileAPI.getRepoInfo(this.props.repoID); const repo = new RepoInfo(res.data); this.setState({ currentRepo: repo }); - this.fetchRepoList(); - }); - } + await this.fetchRepoList(); + } catch (error) { + const errMessage = Utils.getErrorMsg(error); + toaster.danger(errMessage); + } + }; fetchRepoList = async () => { try { diff --git a/frontend/src/components/dialog/select-dirent-body.js b/frontend/src/components/dialog/select-dirent-body.js index 53afa268301..87f892894ed 100644 --- a/frontend/src/components/dialog/select-dirent-body.js +++ b/frontend/src/components/dialog/select-dirent-body.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Button, ModalFooter, ModalBody, Alert, Row, Col } from 'reactstrap'; import RepoListWrapper from '../file-chooser/repo-list-wrapper'; -import { MODE_TYPE_MAP } from '../dialog/move-dirent-dialog'; +import { MODE_TYPE_MAP } from '../../constants'; import { seafileAPI } from '../../utils/seafile-api'; import { gettext } from '../../utils/constants'; import { RepoInfo } from '../../models'; diff --git a/frontend/src/components/dirent-grid-view/dirent-grid-view.js b/frontend/src/components/dirent-grid-view/dirent-grid-view.js index e72e39feeb7..d86ae67b807 100644 --- a/frontend/src/components/dirent-grid-view/dirent-grid-view.js +++ b/frontend/src/components/dirent-grid-view/dirent-grid-view.js @@ -957,6 +957,7 @@ class DirentGridView extends React.Component { selectedDirentList={selectedDirentList} onItemsCopy={this.props.onItemsCopy} onCancelCopy={this.onCopyToggle} + onAddFolder={this.props.onAddFolder} /> } {this.state.isEditFileTagShow && diff --git a/frontend/src/components/dirent-list-view/dirent-list-item.js b/frontend/src/components/dirent-list-view/dirent-list-item.js index d74e75f81ce..e8044016e99 100644 --- a/frontend/src/components/dirent-list-view/dirent-list-item.js +++ b/frontend/src/components/dirent-list-view/dirent-list-item.js @@ -982,6 +982,7 @@ class DirentListItem extends React.Component { onItemCopy={this.props.onItemCopy} onCancelCopy={this.onItemCopyToggle} repoEncrypted={this.props.repoEncrypted} + onAddFolder={this.props.onAddFolder} /> } diff --git a/frontend/src/components/dirent-list-view/dirent-list-view.js b/frontend/src/components/dirent-list-view/dirent-list-view.js index cff1d6492c8..91543ad21e8 100644 --- a/frontend/src/components/dirent-list-view/dirent-list-view.js +++ b/frontend/src/components/dirent-list-view/dirent-list-view.js @@ -872,6 +872,7 @@ class DirentListView extends React.Component { isMultipleOperation={this.state.isMultipleOperation} onItemsCopy={this.props.onItemsCopy} onCancelCopy={this.onCopyToggle} + onAddFolder={this.props.onAddFolder} /> } {this.state.isProgressDialogShow && diff --git a/frontend/src/components/file-chooser/index.js b/frontend/src/components/file-chooser/index.js index ba47129bd22..8d155c8fd5f 100644 --- a/frontend/src/components/file-chooser/index.js +++ b/frontend/src/components/file-chooser/index.js @@ -11,6 +11,7 @@ import { gettext, isPro } from '../../utils/constants'; import { Utils } from '../../utils/utils'; import '../../css/file-chooser.css'; +import { MODE_TYPE_MAP } from '../../constants'; const propTypes = { isShowFile: PropTypes.bool, @@ -18,13 +19,7 @@ const propTypes = { repoID: PropTypes.string, onDirentItemClick: PropTypes.func, onRepoItemClick: PropTypes.func, - mode: PropTypes.oneOf([ - 'current_repo_and_other_repos', - 'only_all_repos', - 'only_current_library', - 'only_other_libraries', - 'recently_used' - ]).isRequired, + mode: PropTypes.isRequired, fileSuffixes: PropTypes.arrayOf(PropTypes.string), currentPath: PropTypes.string, searchResults: PropTypes.array, @@ -128,7 +123,7 @@ class FileChooser extends React.Component { searchInfo: '', searchResults: [], }); - if (this.props.mode === 'only_other_libraries') { + if (this.props.mode === MODE_TYPE_MAP.ONLY_OTHER_LIBRARIES) { this.onOtherRepoToggle(); } } diff --git a/frontend/src/components/file-chooser/repo-list-wrapper.js b/frontend/src/components/file-chooser/repo-list-wrapper.js index 44a414f50fd..8f06670cfbb 100644 --- a/frontend/src/components/file-chooser/repo-list-wrapper.js +++ b/frontend/src/components/file-chooser/repo-list-wrapper.js @@ -5,7 +5,7 @@ import RecentlyUsedListView from './recently-used-list-view'; import { gettext } from '../../utils/constants'; import SearchedListView from './searched-list-view'; import { SearchStatus } from './searcher'; -import { MODE_TYPE_MAP } from '../dialog/move-dirent-dialog'; +import { MODE_TYPE_MAP } from '../../constants'; import Loading from '../loading'; const RepoListWrapper = (props) => { diff --git a/frontend/src/components/file-chooser/searcher/index.js b/frontend/src/components/file-chooser/searcher/index.js index 85912fb2859..9154ef38469 100644 --- a/frontend/src/components/file-chooser/searcher/index.js +++ b/frontend/src/components/file-chooser/searcher/index.js @@ -4,7 +4,7 @@ import { Input } from 'reactstrap'; import { gettext } from '../../../utils/constants'; import { seafileAPI } from '../../../utils/seafile-api'; import { SEARCH_CONTAINER } from '../../../constants/zIndexes'; -import { MODE_TYPE_MAP } from '../../dialog/move-dirent-dialog'; +import { MODE_TYPE_MAP } from '../../../constants'; import './index.css'; diff --git a/frontend/src/components/toolbar/selected-dirents-toolbar.js b/frontend/src/components/toolbar/selected-dirents-toolbar.js index 9b34e6e4c03..8ab1fa7ee1e 100644 --- a/frontend/src/components/toolbar/selected-dirents-toolbar.js +++ b/frontend/src/components/toolbar/selected-dirents-toolbar.js @@ -430,6 +430,7 @@ class SelectedDirentsToolbar extends React.Component { isMultipleOperation={this.state.isMultipleOperation} onItemsCopy={this.props.onItemsCopy} onCancelCopy={this.onCopyToggle} + onAddFolder={this.props.onAddFolder} /> } {this.state.isZipDialogOpen && diff --git a/frontend/src/constants/index.js b/frontend/src/constants/index.js index 2306bee9967..3625e3bec0b 100644 --- a/frontend/src/constants/index.js +++ b/frontend/src/constants/index.js @@ -23,3 +23,12 @@ export const MAP_TYPE = { export const DOMESTIC_MAP_TYPE = [MAP_TYPE.B_MAP]; export { KeyCodes, zIndexes, TAG_COLORS }; + +export const MODE_TYPE_MAP = { + CURRENT_AND_OTHER_REPOS: 'current_repo_and_other_repos', + ONLY_CURRENT_LIBRARY: 'only_current_library', + ONLY_ALL_REPOS: 'only_all_repos', + ONLY_OTHER_LIBRARIES: 'only_other_libraries', + RECENTLY_USED: 'recently_used', + SEARCH_RESULTS: 'search_results', +}; diff --git a/frontend/src/pages/lib-content-view/lib-content-view.js b/frontend/src/pages/lib-content-view/lib-content-view.js index c75f74c5073..a09ee2b12eb 100644 --- a/frontend/src/pages/lib-content-view/lib-content-view.js +++ b/frontend/src/pages/lib-content-view/lib-content-view.js @@ -760,7 +760,7 @@ class LibContentView extends React.Component { }; // toolbar operations - onMoveItems = (destRepo, destDirentPath, moveByDialog = false) => { + onMoveItems = (destRepo, destDirentPath, byDialog = false) => { let repoID = this.props.repoID; let selectedDirentList = this.state.selectedDirentList; @@ -798,7 +798,7 @@ class LibContentView extends React.Component { toaster.success(message); } - if (moveByDialog) { + if (byDialog) { this.updateRecentlyUsedRepos(destRepo, destDirentPath); } @@ -819,7 +819,7 @@ class LibContentView extends React.Component { }); }; - onCopyItems = (destRepo, destDirentPath) => { + onCopyItems = (destRepo, destDirentPath, byDialog = false) => { let repoID = this.props.repoID; let selectedDirentList = this.state.selectedDirentList; @@ -849,6 +849,10 @@ class LibContentView extends React.Component { // show tip message if copy to current repo let message = Utils.getCopySuccessfulMessage(dirNames); toaster.success(message); + + if (byDialog) { + this.updateRecentlyUsedRepos(destRepo, destDirentPath); + } } }).catch((error) => { if (!error.response.data.lib_need_decrypt) { @@ -1244,7 +1248,7 @@ class LibContentView extends React.Component { }; // list operations - onMoveItem = (destRepo, dirent, moveToDirentPath, nodeParentPath, moveByDialog = false) => { + onMoveItem = (destRepo, dirent, moveToDirentPath, nodeParentPath, byDialog = false) => { this.updateCurrentNotExistDirent(dirent); let repoID = this.props.repoID; // just for view list state @@ -1289,7 +1293,7 @@ class LibContentView extends React.Component { toaster.success(message); } - if (moveByDialog) { + if (byDialog) { this.updateRecentlyUsedRepos(destRepo, moveToDirentPath); } @@ -1314,7 +1318,7 @@ class LibContentView extends React.Component { }); }; - onCopyItem = (destRepo, dirent, copyToDirentPath, nodeParentPath) => { + onCopyItem = (destRepo, dirent, copyToDirentPath, nodeParentPath, byDialog = false) => { let repoID = this.props.repoID; // just for view list state let dirName = dirent.name; @@ -1348,6 +1352,10 @@ class LibContentView extends React.Component { let message = gettext('Successfully copied %(name)s.'); message = message.replace('%(name)s', dirName); toaster.success(message); + + if (byDialog) { + this.updateRecentlyUsedRepos(destRepo, copyToDirentPath); + } } }).catch((error) => { if (!error.response.data.lib_need_decrypt) {