diff --git a/frontend/src/components/search/ai-search.js b/frontend/src/components/search/ai-search.js
index 7cac8becdc..33af771e03 100644
--- a/frontend/src/components/search/ai-search.js
+++ b/frontend/src/components/search/ai-search.js
@@ -197,9 +197,36 @@ export default class AISearch extends Component {
onItemClickHandler = (item) => {
this.resetToDefault();
+ this.keepVisitedItem(item);
this.props.onSearchedClick(item);
};
+ keepVisitedItem = (targetItem) => {
+ const { repoID } = this.props;
+ let targetIndex;
+ let storeKey = 'sfVisitedAISearchItems';
+ if (repoID) {
+ storeKey += repoID;
+ }
+ const items = JSON.parse(localStorage.getItem(storeKey)) || [];
+ for (let i = 0, len = items.length; i < len; i++) {
+ const { repo_id, path } = items[i];
+ const { repo_id: targetRepoID, path: targetPath } = targetItem;
+ if (repo_id == targetRepoID && path == targetPath) {
+ targetIndex = i;
+ break;
+ }
+ }
+ if (targetIndex != undefined) {
+ items.splice(targetIndex, 1);
+ }
+ items.unshift(targetItem);
+ if (items.length > 50) { // keep 50 items at most
+ items.pop();
+ }
+ localStorage.setItem(storeKey, JSON.stringify(items));
+ };
+
onChangeHandler = (event) => {
const newValue = event.target.value;
this.setState({ value: newValue }, () => {
@@ -342,11 +369,54 @@ export default class AISearch extends Component {
this.setState({ searchMode: SEARCH_MODE.COMBINED });
}
+ renderVisitedItems = (items) => {
+ const { highlightIndex } = this.state;
+ const results = (
+ <>
+
{gettext('Search results visited recently')}
+
+ {items.map((item, index) => {
+ const isHighlight = index === highlightIndex;
+ return (
+ {this.highlightRef = ref;} : () => {}}
+ />
+ );
+ })}
+
+ >
+ );
+
+ return (
+ <>
+
+ {results}
+
+
+ {results}
+
+ >
+ );
+ }
+
renderSearchResult() {
const { resultItems, highlightIndex, width, searchMode, answeringResult } = this.state;
if (!width || width === 'default') return null;
- if (!this.state.isResultShow) return null;
+ if (!this.state.isResultShow) {
+ const { repoID } = this.props;
+ let storeKey = 'sfVisitedAISearchItems';
+ if (repoID) {
+ storeKey += repoID;
+ }
+ const visitedItems = JSON.parse(localStorage.getItem(storeKey)) || [];
+ return visitedItems.length ? this.renderVisitedItems(visitedItems) : null;
+ }
+
if (!this.state.isResultGetted || getValueLength(this.inputValue) < 3) {
return (
diff --git a/frontend/src/components/search/search.js b/frontend/src/components/search/search.js
index fe8c3d7f2c..f03091af4f 100644
--- a/frontend/src/components/search/search.js
+++ b/frontend/src/components/search/search.js
@@ -163,9 +163,36 @@ class Search extends Component {
onItemClickHandler = (item) => {
this.resetToDefault();
+ this.keepVisitedItem(item);
this.props.onSearchedClick(item);
};
+ keepVisitedItem = (targetItem) => {
+ const { repoID } = this.props;
+ let targetIndex;
+ let storeKey = 'sfVisitedSearchItems';
+ if (repoID) {
+ storeKey += repoID;
+ }
+ const items = JSON.parse(localStorage.getItem(storeKey)) || [];
+ for (let i = 0, len = items.length; i < len; i++) {
+ const { repo_id, path } = items[i];
+ const { repo_id: targetRepoID, path: targetPath } = targetItem;
+ if (repo_id == targetRepoID && path == targetPath) {
+ targetIndex = i;
+ break;
+ }
+ }
+ if (targetIndex != undefined) {
+ items.splice(targetIndex, 1);
+ }
+ items.unshift(targetItem);
+ if (items.length > 50) { // keep 50 items at most
+ items.pop();
+ }
+ localStorage.setItem(storeKey, JSON.stringify(items));
+ };
+
onChangeHandler = (event) => {
const newValue = event.target.value;
this.setState({ value: newValue }, () => {
@@ -341,7 +368,16 @@ class Search extends Component {
const { resultItems, highlightIndex, width } = this.state;
if (!width || width === 'default') return null;
- if (!this.state.isResultShow) return null;
+ if (!this.state.isResultShow) {
+ const { repoID } = this.props;
+ let storeKey = 'sfVisitedSearchItems';
+ if (repoID) {
+ storeKey += repoID;
+ }
+ const visitedItems = JSON.parse(localStorage.getItem(storeKey)) || [];
+ return visitedItems.length ? this.renderResults(visitedItems, true) : null;
+ }
+
if (!this.state.isResultGetted || getValueLength(this.inputValue) < 3) {
return (
@@ -353,7 +389,14 @@ class Search extends Component {
);
}
+ return this.renderResults(resultItems);
+ }
+
+ renderResults = (resultItems, isVisited) => {
+ const { highlightIndex } = this.state;
const results = (
+ <>
+ {isVisited && {gettext('Search results visited recently')}
}
{resultItems.map((item, index) => {
const isHighlight = index === highlightIndex;
@@ -368,6 +411,7 @@ class Search extends Component {
);
})}
+ >
);
return (
diff --git a/frontend/src/css/search.css b/frontend/src/css/search.css
index dcb7de2e27..64f049410b 100644
--- a/frontend/src/css/search.css
+++ b/frontend/src/css/search.css
@@ -346,3 +346,10 @@
.search-result-container .search-mode-similarity-index-status.index-status-uncreated {
cursor: pointer;
}
+
+.visited-search-results-title {
+ color: #999;
+ font-size: .875rem;
+ font-weight: normal;
+ margin: 7px 0 10px;
+}