diff --git a/frontend/src/components/search/details/index.js b/frontend/src/components/search/details/index.js index d38c78df32..6d12d9e804 100644 --- a/frontend/src/components/search/details/index.js +++ b/frontend/src/components/search/details/index.js @@ -34,6 +34,11 @@ const SearchedItemDetails = ({ repoID, path, dirent }) => { const err = gettext('Library does not exist'); setRepoInfo(null); setLibErrorMessage(err); + + const storeKey = 'sfVisitedSearchItems' + repoID; + const visitedItems = JSON.parse(localStorage.getItem(storeKey)) || []; + const filteredItems = visitedItems.filter(item => item.repo_id !== repoID); + localStorage.setItem(storeKey, JSON.stringify(filteredItems)); } else { const errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); @@ -46,7 +51,7 @@ const SearchedItemDetails = ({ repoID, path, dirent }) => { controller.abort(); clearTimeout(timer); }; - }, [repoID]); + }, [repoID, path]); useEffect(() => { setDirentDetail(null); @@ -73,6 +78,12 @@ const SearchedItemDetails = ({ repoID, path, dirent }) => { return; // Ignore abort errors } if (error.response && error.response.status === 404) { + const storeKey = 'sfVisitedSearchItems' + repoID; + const visitedItems = JSON.parse(localStorage.getItem(storeKey)) || []; + const filteredItems = visitedItems.filter(item => + item.path !== path || item.repo_id !== repoID + ); + localStorage.setItem(storeKey, JSON.stringify(filteredItems)); const err = `${dirent.type === 'file' ? 'File' : 'Folder'} does not exist`; setErrMessage(err); return; diff --git a/frontend/src/components/search/search-result-item.js b/frontend/src/components/search/search-result-item.js index 98255e35c5..d6f5354f3c 100644 --- a/frontend/src/components/search/search-result-item.js +++ b/frontend/src/components/search/search-result-item.js @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { Utils } from '../../utils/utils'; +import { gettext } from '../../utils/constants'; const propTypes = { item: PropTypes.object.isRequired, @@ -12,6 +13,7 @@ const propTypes = { onHighlightIndex: PropTypes.func, timer: PropTypes.number, onSetTimer: PropTypes.func, + onDeleteItem: PropTypes.func, }; class SearchResultItem extends React.Component { @@ -37,6 +39,13 @@ class SearchResultItem extends React.Component { } }; + deleteItem = (e) => { + e.stopPropagation(); + if (this.props.onDeleteItem) { + this.props.onDeleteItem(this.props.item); + } + }; + render() { const { item, setRef = (() => {}) } = this.props; let folderIconUrl = item.link_content ? Utils.getFolderIconUrl(false, 192) : Utils.getDefaultLibIconUrl(); @@ -61,6 +70,15 @@ class SearchResultItem extends React.Component {
{showName}
+ {this.props.isHighlight && ( + + )} ); } diff --git a/frontend/src/components/search/search.js b/frontend/src/components/search/search.js index 3a67f96ca9..b1f99122c0 100644 --- a/frontend/src/components/search/search.js +++ b/frontend/src/components/search/search.js @@ -69,6 +69,7 @@ class Search extends Component { }, suffixes: '', }, + visitedItems: [], }; this.highlightRef = null; this.source = null; // used to cancel request; @@ -86,7 +87,9 @@ class Search extends Component { document.addEventListener('compositionstart', this.onCompositionStart); document.addEventListener('compositionend', this.onCompositionEnd); const isFiltersShow = localStorage.getItem(SEARCH_FILTERS_SHOW_KEY) === 'true'; - this.setState({ isFiltersShow }); + const visitedItems = JSON.parse(localStorage.getItem(this.storeKey)) || []; + + this.setState({ isFiltersShow, visitedItems }); } UNSAFE_componentWillReceiveProps(nextProps) { @@ -94,6 +97,15 @@ class Search extends Component { this.isChineseInput = false; } + componentDidUpdate(prevProps, prevState) { + if (this.state.isMaskShow && !prevState.isMaskShow) { + const visitedItems = JSON.parse(localStorage.getItem(this.storeKey)) || []; + if (visitedItems !== prevState.visitedItems) { + this.setState({ visitedItems }); + } + } + } + componentWillUnmount() { document.removeEventListener('keydown', this.onDocumentKeydown); document.removeEventListener('compositionstart', this.onCompositionStart); @@ -556,11 +568,10 @@ class Search extends Component { }; renderSearchResult() { - const { resultItems, width, showRecent, isResultGotten, isLoading } = this.state; + const { resultItems, width, showRecent, isResultGotten, isLoading, visitedItems } = this.state; if (!width || width === 'default') return null; if (showRecent) { - const visitedItems = JSON.parse(localStorage.getItem(this.storeKey)) || []; if (visitedItems.length > 0) { return this.renderResults(visitedItems, true); } @@ -734,6 +745,13 @@ class Search extends Component { this.setState({ highlightIndex: index }); }, 200); + deleteItem = (item) => { + const { visitedItems } = this.state; + const update = visitedItems.filter(i => i.path !== item.path || i.repo_id !== item.repo_id); + this.setState({ visitedItems: update }); + localStorage.setItem(this.storeKey, JSON.stringify(update)); + }; + renderResults = (resultItems, isVisited) => { const { highlightIndex } = this.state; @@ -758,6 +776,7 @@ class Search extends Component { onHighlightIndex={this.debounceHighlight} timer={this.timer} onSetTimer={(timer) => {this.timer = timer;}} + onDeleteItem={this.deleteItem} /> ); })} diff --git a/frontend/src/css/search.css b/frontend/src/css/search.css index dac1f02295..9dca4af4b4 100644 --- a/frontend/src/css/search.css +++ b/frontend/src/css/search.css @@ -188,6 +188,16 @@ overflow: hidden; } +.search-result-item .search-icon-right { + display: flex; + justify-content: center; + align-items: center; +} + +.search-result-item .search-icon-right:hover { + background-color: #dbdbdb; +} + .item-content .item-name a { color: #EA8102 !important; }