diff --git a/frontend/src/components/file-chooser/file-chooser.js b/frontend/src/components/file-chooser/file-chooser.js index 24cf50c1e4..efafa791ea 100644 --- a/frontend/src/components/file-chooser/file-chooser.js +++ b/frontend/src/components/file-chooser/file-chooser.js @@ -57,6 +57,8 @@ class FileChooser extends React.Component { searchInfo: '', searchResults: [], selectedItemInfo: {}, + isBrowsing: false, + browsingPath: '', }; this.inputValue = ''; this.timer = null; @@ -115,6 +117,8 @@ class FileChooser extends React.Component { this.setState({ isSearching: false, isResultGot: false, + isBrowsing: false, + browsingPath: '', searchInfo: '', searchResults: [], }); @@ -182,8 +186,12 @@ class FileChooser extends React.Component { this.setState({ isSearching: false, isResultGot: false, + isBrowsing: false, + browsingPath: '', searchInfo: '', searchResults: [], + selectedPath: this.props.currentPath, + selectedItemInfo: {}, }); this.inputValue = ''; this.timer = null; @@ -204,7 +212,7 @@ class FileChooser extends React.Component { } this.inputValue = searchInfo; - if (this.inputValue === '' || this.getValueLength(this.inputValue) < 3) { + if (this.inputValue === '') { this.setState({ isResultGot: false, }); @@ -242,18 +250,27 @@ class FileChooser extends React.Component { }; sendRequest = (queryData, cancelToken) => { + const filterCurrentRepo = (results) => { + if (this.props.mode === 'only_other_libraries') { + return results.filter(item => item.repo_id !== this.state.currentRepoInfo.repo_id); + } + return results; + }; + if (isPro && enableSeasearch && !enableElasticsearch) { seafileAPI.aiSearchFiles(queryData, cancelToken).then(res => { + const filteredResults = filterCurrentRepo(res.data.results.filter(item => item.is_dir)); this.setState({ - searchResults: res.data.results.length > 0 ? this.formatResultItems(res.data.results.filter(item => item.is_dir)) : [], + searchResults: filteredResults.length > 0 ? this.formatResultItems(filteredResults) : [], isResultGot: true }); this.source = null; }); } else { seafileAPI.searchFiles(queryData, cancelToken).then(res => { + const filteredResults = filterCurrentRepo(res.data.results); this.setState({ - searchResults: res.data.total ? this.formatResultItems(res.data.results) : [], + searchResults: res.data.total ? this.formatResultItems(filteredResults) : [], isResultGot: true }); this.source = null; @@ -306,10 +323,6 @@ class FileChooser extends React.Component { }; renderSearchedView = () => { - if (this.getValueLength(this.inputValue) < 3) { - return ''; - } - if (!this.state.isResultGot) { return (); } @@ -409,7 +422,16 @@ class FileChooser extends React.Component { } } - this.onCloseSearching(); + this.setState({ + isSearching: false, + isResultGot: false, + searchResults: [], + isBrowsing: true, + browsingPath: item.path.substring(0, item.path.length - 1), + }); + this.inputValue = ''; + this.timer = null; + this.source = null; }; onScroll = (event) => { event.stopPropagation(); @@ -481,6 +503,8 @@ class FileChooser extends React.Component { isShowFile={isShowFile} fileSuffixes={fileSuffixes} selectedItemInfo={selectedItemInfo} + isBrowsing={this.state.isBrowsing} + browsingPath={this.state.browsingPath} /> )} @@ -517,6 +541,8 @@ class FileChooser extends React.Component { isShowFile={isShowFile} fileSuffixes={fileSuffixes} selectedItemInfo={selectedItemInfo} + isBrowsing={this.state.isBrowsing} + browsingPath={this.state.browsingPath} /> )} @@ -534,7 +560,7 @@ class FileChooser extends React.Component { }; render() { - const { repoID } = this.props; + const { repoID, mode } = this.props; const { selectedRepo, searchInfo, isSearching } = this.state; if (!selectedRepo && repoID) { @@ -543,7 +569,7 @@ class FileChooser extends React.Component { return ( - {isPro && this.props.mode !== 'recently_used' && ( + {isPro && mode !== 'recently_used' && (
{searchInfo.length !== 0 && ( diff --git a/frontend/src/components/file-chooser/repo-list-item.js b/frontend/src/components/file-chooser/repo-list-item.js index 8398f71080..b1b4b69c1a 100644 --- a/frontend/src/components/file-chooser/repo-list-item.js +++ b/frontend/src/components/file-chooser/repo-list-item.js @@ -21,6 +21,8 @@ const propTypes = { fileSuffixes: PropTypes.array, selectedItemInfo: PropTypes.object, hideLibraryName: PropTypes.bool, + isBrowsing: PropTypes.bool, + browsingPath: PropTypes.string, }; class RepoListItem extends React.Component { @@ -63,6 +65,30 @@ class RepoListItem extends React.Component { } } + componentDidUpdate(prevProps) { + if (prevProps.isBrowsing && !this.props.isBrowsing) { + this.setState({ + treeData: treeHelper.buildTree(), + isShowChildren: this.props.initToShowChildren, + }); + + const { isCurrentRepo, currentPath, repo, selectedItemInfo } = this.props; + const { repoID } = selectedItemInfo || {}; + + if (isCurrentRepo && !repoID) { + this.loadRepoDirentList(repo); + setTimeout(() => { + const repoID = repo.repo_id; + if (isCurrentRepo && currentPath && currentPath != '/') { + const expandNode = true; + this.loadNodeAndParentsByPath(repoID, currentPath, expandNode); + } + }, 0); + } + } + + } + componentWillUnmount() { this.setState({ isMounted: false }); } @@ -205,7 +231,7 @@ class RepoListItem extends React.Component { return (
  • - {!this.props.hideLibraryName && + {!this.props.hideLibraryName && !this.props.isBrowsing &&
    @@ -228,6 +254,8 @@ class RepoListItem extends React.Component { treeData={this.state.treeData} onNodeCollapse={this.onNodeCollapse} onNodeExpanded={this.onNodeExpanded} + isBrowsing={this.props.isBrowsing} + browsingPath={this.props.browsingPath} /> )}
  • diff --git a/frontend/src/components/file-chooser/repo-list-view.js b/frontend/src/components/file-chooser/repo-list-view.js index be8bda9f19..a41ea90562 100644 --- a/frontend/src/components/file-chooser/repo-list-view.js +++ b/frontend/src/components/file-chooser/repo-list-view.js @@ -15,6 +15,8 @@ const propTypes = { fileSuffixes: PropTypes.array, selectedItemInfo: PropTypes.object, currentPath: PropTypes.string, + isBrowsing: PropTypes.bool, + browsingPath: PropTypes.string, }; class RepoListView extends React.Component { @@ -43,6 +45,8 @@ class RepoListView extends React.Component { isShowFile={this.props.isShowFile} fileSuffixes={this.props.fileSuffixes} selectedItemInfo={this.props.selectedItemInfo} + isBrowsing={this.props.isBrowsing} + browsingPath={this.props.browsingPath} /> ); })} diff --git a/frontend/src/components/file-chooser/searched-list-view.js b/frontend/src/components/file-chooser/searched-list-view.js index a04fc88321..754809e3f6 100644 --- a/frontend/src/components/file-chooser/searched-list-view.js +++ b/frontend/src/components/file-chooser/searched-list-view.js @@ -13,12 +13,44 @@ class SearchedListView extends React.Component { constructor(props) { super(props); this.state = { - currentItem: null, + currentItem: props.searchResults.length > 0 ? props.searchResults[0] : null, + currentIndex: props.searchResults.length > 0 ? 0 : -1, }; + this.itemRef = React.createRef(); } - onItemClick = (item) => { - this.setState({ currentItem: item }); + componentDidMount() { + document.addEventListener('keydown', this.handleKeyDown); + } + + componentWillUnmount() { + document.removeEventListener('keydown', this.handleKeyDown); + } + + handleKeyDown = (event) => { + const { searchResults } = this.props; + const { currentIndex } = this.state; + + if (event.key === 'ArrowDown') { + const nextIndex = (currentIndex + 1) % searchResults.length; + this.setState({ + currentItem: searchResults[nextIndex], + currentIndex: nextIndex, + }); + } else if (event.key === 'ArrowUp') { + const prevIndex = (currentIndex - 1 + searchResults.length) % searchResults.length; + this.setState({ + currentItem: searchResults[prevIndex], + currentIndex: prevIndex, + }); + } else if (event.key === 'Enter') { + this.onItemClick(searchResults[currentIndex], currentIndex); + this.props.onSearchedItemDoubleClick(searchResults[currentIndex]); + } + }; + + onItemClick = (item, index) => { + this.setState({ currentItem: item, currentIndex: index }); this.props.onItemClick(item); }; @@ -36,9 +68,10 @@ class SearchedListView extends React.Component { return ( this.onItemClick(item, index)} onSearchedItemDoubleClick={this.props.onSearchedItemDoubleClick} /> ); diff --git a/frontend/src/components/file-chooser/tree-list-item.js b/frontend/src/components/file-chooser/tree-list-item.js index 96ea448b4e..9825ced1c2 100644 --- a/frontend/src/components/file-chooser/tree-list-item.js +++ b/frontend/src/components/file-chooser/tree-list-item.js @@ -12,6 +12,7 @@ const propTypes = { filePath: PropTypes.string, fileSuffixes: PropTypes.array, level: PropTypes.number, + isBrowsing: PropTypes.bool, }; class TreeViewItem extends React.Component { @@ -96,7 +97,7 @@ class TreeViewItem extends React.Component { if (this.props.selectedRepo) { isCurrentRepo = this.props.selectedRepo.repo_id === this.props.repo.repo_id; } - let isCurrentPath = this.props.selectedPath === this.state.filePath; + let isCurrentPath = this.props.selectedPath === this.state.filePath || this.props.selectedPath === node.path; const fileName = node.object.name; if (this.props.fileSuffixes && fileName && node.object.type === 'file') { diff --git a/frontend/src/components/file-chooser/tree-list-view.js b/frontend/src/components/file-chooser/tree-list-view.js index b3e551729a..d23c8f7767 100644 --- a/frontend/src/components/file-chooser/tree-list-view.js +++ b/frontend/src/components/file-chooser/tree-list-view.js @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import TreeListItem from './tree-list-item'; +import treeHelper from '../tree-view/tree-helper'; const propTypes = { selectedPath: PropTypes.string, @@ -11,22 +12,42 @@ const propTypes = { onNodeCollapse: PropTypes.func.isRequired, onNodeExpanded: PropTypes.func.isRequired, fileSuffixes: PropTypes.array, + isBrowsing: PropTypes.bool, + browsingPath: PropTypes.string, }; class TreeListView extends React.Component { render() { + const { + isBrowsing, + browsingPath, + treeData, + selectedPath, + onNodeCollapse, + onNodeExpanded, + repo, + onDirentItemClick, + selectedRepo, + fileSuffixes + } = this.props; + + const browsingNode = treeHelper.findNodeByPath(treeData, browsingPath); + if (isBrowsing && !browsingNode) return null; + + const node = isBrowsing ? browsingNode : treeData.root; + return (