From f02a10d2636cd5eb00467f43b1de990f7c1b2b74 Mon Sep 17 00:00:00 2001 From: cir9no <44470218+cir9no@users.noreply.github.com> Date: Wed, 12 Jun 2024 19:04:49 +0800 Subject: [PATCH] feat/search: add search items sup --- frontend/src/components/search/ai-search.js | 105 +++++++++++++++++--- frontend/src/components/search/search.js | 98 +++++++++++++++--- frontend/src/css/search.css | 17 ++++ seahub/ai/apis.py | 50 +++++++++- seahub/ai/utils.py | 2 + seahub/urls.py | 3 +- 6 files changed, 244 insertions(+), 31 deletions(-) diff --git a/frontend/src/components/search/ai-search.js b/frontend/src/components/search/ai-search.js index c4647a4f8ab..a59e5efa3ee 100644 --- a/frontend/src/components/search/ai-search.js +++ b/frontend/src/components/search/ai-search.js @@ -26,6 +26,11 @@ const SEARCH_MODE = { COMBINED: 'combined-search', }; +const SEARCH_TAB = { + GLOBAL: 'global', + LIBRARY: 'library', +}; + const PER_PAGE = 10; const controlKey = isMac() ? '⌘' : 'Ctrl'; @@ -59,6 +64,8 @@ export default class AISearch extends Component { searchPageUrl: this.baseSearchPageURL, indexState: '', searchMode: SEARCH_MODE.COMBINED, + selectedSearchTab: SEARCH_TAB.GLOBAL, + isFocused: false, }; this.inputValue = ''; this.highlightRef = null; @@ -151,7 +158,7 @@ export default class AISearch extends Component { }; onFocusHandler = () => { - this.setState({ width: '570px', isMaskShow: true, isCloseShow: true }); + this.setState({ width: '570px', isMaskShow: true, isCloseShow: true, isFocused: true }); }; onCloseHandler = () => { @@ -210,8 +217,9 @@ export default class AISearch extends Component { keepVisitedItem = (targetItem) => { const { repoID } = this.props; + const { selectedSearchTab } = this.state; let targetIndex; - let storeKey = 'sfVisitedAISearchItems'; + let storeKey = selectedSearchTab === 'global' ? 'sfVisitedSearchItems' : 'sfVisitedLibraryItems'; if (repoID) { storeKey += repoID; } @@ -258,7 +266,7 @@ export default class AISearch extends Component { }; onSearch = () => { - const { value } = this.state; + const { value, selectedSearchTab } = this.state; const { repoID } = this.props; if (this.inputValue === '' || getValueLength(this.inputValue) < 3) { this.setState({ @@ -273,8 +281,11 @@ export default class AISearch extends Component { search_repo: repoID ? repoID : 'all', search_ftypes: 'all', }; - this.getSearchResult(queryData); - }; + if (selectedSearchTab === SEARCH_TAB.GLOBAL) { + this.getSearchResult(queryData); + } else if (selectedSearchTab === SEARCH_TAB.LIBRARY) { + this.getRepoSearchResult(value); + } }; getSearchResult = (queryData) => { if (this.source) { @@ -315,6 +326,34 @@ export default class AISearch extends Component { }); }; + getRepoSearchResult = (query_str) => { + if (this.source) { + this.source.cancel('prev request is cancelled'); + } + this.setState({ + isResultGetted: false, + resultItems: [], + highlightIndex: 0, + }); + this.source = seafileAPI.getSource(); + + const query_type = SEARCH_TAB.LIBRARY + let results = []; + seafileAPI.searchItems(query_str, query_type, this.source.token).then(res => { + results = [...results, ...this.formatResultItems(res.data.results)]; + this.setState({ + resultItems: results, + isResultGetted: true, + isLoading: false, + hasMore: false, + }); + }).catch(error => { + /* eslint-disable */ + console.log(error); + this.setState({ isLoading: false }); + }); + }; + onResultListScroll = (e) => { // Load less than 100 results if (!this.state.hasMore || this.state.isLoading || this.state.resultItems.length > 100) { @@ -367,7 +406,36 @@ export default class AISearch extends Component { highlightIndex: 0, isSearchInputShow: false, showRecent: true, + isFocused: false + }); + } + + onTabChange = (selectedSearchTab) => { + this.setState({ selectedSearchTab, resultItems: [], isResultGetted: false }, () => { + this.onSearch(); }); + }; + + renderTabs() { + const { selectedSearchTab } = this.state; + return ( +
+
this.onTabChange('global')} + > + Global +
+
this.onTabChange('library')} + > + Library +
+
+ ); } openAsk = () => { @@ -415,12 +483,12 @@ export default class AISearch extends Component { } renderSearchResult() { - const { resultItems, highlightIndex, width, isResultGetted } = this.state; + const { resultItems, highlightIndex, width, isResultGetted, selectedSearchTab } = this.state; if (!width || width === 'default') return null; - if (this.state.showRecent) { + if ((selectedSearchTab === SEARCH_TAB.GLOBAL || selectedSearchTab === SEARCH_TAB.LIBRARY) && this.state.showRecent) { const { repoID } = this.props; - let storeKey = 'sfVisitedAISearchItems'; + let storeKey = selectedSearchTab === 'global' ? 'sfVisitedSearchItems' : 'sfVisitedLibraryItems'; if (repoID) { storeKey += repoID; } @@ -444,12 +512,13 @@ export default class AISearch extends Component { else if (!resultItems.length) { return ( <> -
  • - -
    -
    {gettext('Ask AI')}{': '}{this.state.value.trim()}
    -
    -
  • + {selectedSearchTab === SEARCH_TAB.GLOBAL && ( +
  • + +
    +
    {gettext('Ask AI')}{': '}{this.state.value.trim()}
    +
    +
  • )}
    {gettext('No results matching')}
    ); @@ -457,6 +526,7 @@ export default class AISearch extends Component { const results = (