[Code] Apply default search scope into fetching suggestions (#36494)

* [Code] adjust setup guide

* [Code] Apply default search scope into fetching suggestions

* [Code] Refactor the search bar in the source view page to use the same searchbar in other pages
This commit is contained in:
Mengwei Ding 2019-05-15 06:06:34 -07:00 committed by Fuyao Zhao
parent 5f56c30ffd
commit ef8d7ea905
14 changed files with 81 additions and 197 deletions

View file

@ -153,3 +153,9 @@ export enum SearchScope {
REPOSITORY = 'repository', // Only search repositories REPOSITORY = 'repository', // Only search repositories
FILE = 'file', // Only search files FILE = 'file', // Only search files
} }
export interface SearchOptions {
repoScope: Repository[];
defaultRepoScopeOn: boolean;
defaultRepoScope?: Repository;
}

View file

@ -5,7 +5,7 @@
*/ */
import { createAction } from 'redux-actions'; import { createAction } from 'redux-actions';
import { DocumentSearchResult, Repository, SearchScope } from '../../model'; import { DocumentSearchResult, Repository, SearchOptions, SearchScope } from '../../model';
export interface DocumentSearchPayload { export interface DocumentSearchPayload {
query: string; query: string;
@ -19,11 +19,6 @@ export interface RepositorySearchPayload {
query: string; query: string;
} }
export interface SearchOptions {
repoScope: Repository[];
defaultRepoScopeOn: boolean;
}
// For document search page // For document search page
export const documentSearch = createAction<DocumentSearchPayload>('DOCUMENT SEARCH'); export const documentSearch = createAction<DocumentSearchPayload>('DOCUMENT SEARCH');
export const documentSearchSuccess = createAction<DocumentSearchResult>('DOCUMENT SEARCH SUCCESS'); export const documentSearchSuccess = createAction<DocumentSearchResult>('DOCUMENT SEARCH SUCCESS');
@ -45,7 +40,7 @@ export const repositoryTypeaheadSearchFailed = createAction<string>('REPOSITORY
export const saveSearchOptions = createAction<SearchOptions>('SAVE SEARCH OPTIONS'); export const saveSearchOptions = createAction<SearchOptions>('SAVE SEARCH OPTIONS');
export const turnOnDefaultRepoScope = createAction('TURN ON DEFAULT REPO SCOPE'); export const turnOnDefaultRepoScope = createAction<Repository>('TURN ON DEFAULT REPO SCOPE');
export const searchReposForScope = createAction<RepositorySearchPayload>('SEARCH REPOS FOR SCOPE'); export const searchReposForScope = createAction<RepositorySearchPayload>('SEARCH REPOS FOR SCOPE');
export const searchReposForScopeSuccess = createAction<any>('SEARCH REPOS FOR SCOPE SUCCESS'); export const searchReposForScopeSuccess = createAction<any>('SEARCH REPOS FOR SCOPE SUCCESS');

View file

@ -10,8 +10,8 @@ import { connect } from 'react-redux';
import { RouteComponentProps, withRouter } from 'react-router-dom'; import { RouteComponentProps, withRouter } from 'react-router-dom';
import url from 'url'; import url from 'url';
import { EuiTab, EuiTabs } from '@elastic/eui'; import { EuiTab, EuiTabs } from '@elastic/eui';
import { Repository, SearchScope } from '../../../model'; import { Repository, SearchOptions, SearchScope } from '../../../model';
import { changeSearchScope, SearchOptions } from '../../actions'; import { changeSearchScope } from '../../actions';
import { RootState } from '../../reducers'; import { RootState } from '../../reducers';
import { SearchBar } from '../search_bar'; import { SearchBar } from '../search_bar';
import { EmptyProject } from './empty_project'; import { EmptyProject } from './empty_project';
@ -126,9 +126,10 @@ class AdminPage extends React.PureComponent<Props, State> {
<div className="codeContainer__rootInner"> <div className="codeContainer__rootInner">
<div className="codeContainer__adminWrapper"> <div className="codeContainer__adminWrapper">
<SearchBar <SearchBar
repoScope={this.props.searchOptions.repoScope.map(r => r.uri)} searchOptions={this.props.searchOptions}
query={this.props.query} query={this.props.query}
onSearchScopeChanged={this.props.onSearchScopeChanged} onSearchScopeChanged={this.props.onSearchScopeChanged}
enableSubmitWhenOptionsChanged={false}
ref={element => (this.searchBar = element)} ref={element => (this.searchBar = element)}
/> />
<div className="codeContainer__adminMain"> <div className="codeContainer__adminMain">

View file

@ -141,10 +141,11 @@ class SetupGuidePage extends React.PureComponent<{ setupOk?: boolean }, { hideTo
)} )}
{this.props.setupOk === true && ( {this.props.setupOk === true && (
<React.Fragment> <React.Fragment>
<EuiSpacer size="xs" /> <EuiSpacer size="s" />
<EuiButton iconType="sortLeft"> <EuiButton iconType="sortLeft">
<Link to="/admin">Back To Project Dashboard</Link> <Link to="/admin">Back To Project Dashboard</Link>
</EuiButton> </EuiButton>
<EuiSpacer size="s" />
</React.Fragment> </React.Fragment>
)} )}
<EuiPanel> <EuiPanel>
@ -158,7 +159,7 @@ class SetupGuidePage extends React.PureComponent<{ setupOk?: boolean }, { hideTo
</div> </div>
); );
} }
return <div className="condeContainer__setup">{setup}</div>; return <div className="codeContainer__setup">{setup}</div>;
} }
} }

View file

@ -14,7 +14,7 @@ import { CommitDiff, FileDiff } from '../../../common/git_diff';
import { SearchScope } from '../../../model'; import { SearchScope } from '../../../model';
import { changeSearchScope } from '../../actions'; import { changeSearchScope } from '../../actions';
import { RootState } from '../../reducers'; import { RootState } from '../../reducers';
import { SearchBar } from '../search_bar'; // import { SearchBar } from '../search_bar';
import { DiffEditor } from './diff_editor'; import { DiffEditor } from './diff_editor';
const COMMIT_ID_LENGTH = 16; const COMMIT_ID_LENGTH = 16;
@ -191,11 +191,11 @@ export class DiffPage extends React.Component<Props> {
)); ));
return ( return (
<div className="diff"> <div className="diff">
<SearchBar {/* <SearchBar
repoScope={this.props.repoScope} repoScope={this.props.repoScope}
query={this.props.query} query={this.props.query}
onSearchScopeChanged={this.props.onSearchScopeChanged} onSearchScopeChanged={this.props.onSearchScopeChanged}
/> /> */}
{topBar} {topBar}
<Container> <Container>
<EuiText>{commit.commit.message}</EuiText> <EuiText>{commit.commit.message}</EuiText>

View file

@ -17,12 +17,12 @@ import { RepositoryUtils } from '../../../common/repository_utils';
import { import {
FileTree, FileTree,
FileTreeItemType, FileTreeItemType,
SearchOptions,
SearchScope, SearchScope,
WorkerReservedProgress, WorkerReservedProgress,
Repository,
} from '../../../model'; } from '../../../model';
import { CommitInfo, ReferenceInfo } from '../../../model/commit'; import { CommitInfo, ReferenceInfo } from '../../../model/commit';
import { changeSearchScope, FetchFileResponse, SearchOptions } from '../../actions'; import { changeSearchScope, FetchFileResponse } from '../../actions';
import { MainRouteParams, PathTypes } from '../../common/types'; import { MainRouteParams, PathTypes } from '../../common/types';
import { RepoState, RepoStatus, RootState } from '../../reducers'; import { RepoState, RepoStatus, RootState } from '../../reducers';
import { import {
@ -53,8 +53,8 @@ interface Props extends RouteComponentProps<MainRouteParams> {
onSearchScopeChanged: (s: SearchScope) => void; onSearchScopeChanged: (s: SearchScope) => void;
repoScope: string[]; repoScope: string[];
searchOptions: SearchOptions; searchOptions: SearchOptions;
currentRepository?: Repository;
fileTreeLoading: boolean; fileTreeLoading: boolean;
query: string;
} }
const LANG_MD = 'markdown'; const LANG_MD = 'markdown';
@ -224,12 +224,12 @@ class CodeContent extends React.PureComponent<Props> {
return ( return (
<div className="codeContainer__main"> <div className="codeContainer__main">
<TopBar <TopBar
defaultSearchScope={this.props.currentRepository}
routeParams={this.props.match.params} routeParams={this.props.match.params}
onSearchScopeChanged={this.props.onSearchScopeChanged} onSearchScopeChanged={this.props.onSearchScopeChanged}
buttons={this.renderButtons()} buttons={this.renderButtons()}
searchOptions={this.props.searchOptions} searchOptions={this.props.searchOptions}
branches={this.props.branches} branches={this.props.branches}
query={this.props.query}
/> />
{this.renderContent()} {this.renderContent()}
</div> </div>
@ -395,7 +395,7 @@ const mapStateToProps = (state: RootState) => ({
loadingCommits: state.file.loadingCommits, loadingCommits: state.file.loadingCommits,
repoStatus: statusSelector(state, repoUriSelector(state)), repoStatus: statusSelector(state, repoUriSelector(state)),
searchOptions: state.search.searchOptions, searchOptions: state.search.searchOptions,
currentRepository: state.repository.currentRepository, query: state.search.query,
}); });
const mapDispatchToProps = { const mapDispatchToProps = {

View file

@ -1,128 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { ParsedUrlQuery } from 'querystring';
import React from 'react';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import url from 'url';
import { unique } from 'lodash';
import { SearchScope, Repository } from '../../../model';
import { MainRouteParams, SearchScopeText } from '../../common/types';
import {
AutocompleteSuggestion,
FileSuggestionsProvider,
QueryBar,
RepositorySuggestionsProvider,
SymbolSuggestionsProvider,
} from '../query_bar';
import { Shortcut } from '../shortcuts';
import { SearchOptions } from '../../actions';
interface Props extends RouteComponentProps<MainRouteParams> {
onSearchScopeChanged: (s: SearchScope) => void;
searchOptions: SearchOptions;
defaultSearchScope?: Repository;
}
// TODO(mengwei): refactor this with the SearchBar in ../search_bar/
export class CodeSearchBar extends React.Component<Props> {
public state = {
searchScope: SearchScope.DEFAULT,
};
public queryBar: any | null = null;
public suggestionProviders = [
new SymbolSuggestionsProvider(),
new FileSuggestionsProvider(),
new RepositorySuggestionsProvider(),
];
public onSubmit = (queryString: string) => {
const { history } = this.props;
if (queryString.trim().length === 0) {
return;
}
const query: ParsedUrlQuery = {
q: queryString,
};
if (this.props.searchOptions.repoScope) {
// search from a repo page may have a default scope of this repo
if (this.props.searchOptions.defaultRepoScopeOn && this.props.defaultSearchScope) {
query.repoScope = unique([
...this.props.searchOptions.repoScope.map(r => r.uri),
this.props.defaultSearchScope.uri,
]).join(',');
} else {
query.repoScope = this.props.searchOptions.repoScope.map(r => r.uri).join(',');
}
}
if (this.state.searchScope === SearchScope.REPOSITORY) {
query.scope = SearchScope.REPOSITORY;
}
history.push(url.format({ pathname: '/search', query }));
};
public onSelect = (item: AutocompleteSuggestion) => {
this.props.history.push(item.selectUrl);
};
public render() {
return (
<div className="codeContainer__searchBar">
<Shortcut
keyCode="p"
help={SearchScopeText[SearchScope.REPOSITORY]}
onPress={() => {
this.props.onSearchScopeChanged(SearchScope.REPOSITORY);
if (this.queryBar) {
this.queryBar.focusInput();
}
}}
/>
<Shortcut
keyCode="y"
help={SearchScopeText[SearchScope.SYMBOL]}
onPress={() => {
this.props.onSearchScopeChanged(SearchScope.SYMBOL);
if (this.queryBar) {
this.queryBar.focusInput();
}
}}
/>
<Shortcut
keyCode="s"
help={SearchScopeText[SearchScope.DEFAULT]}
onPress={() => {
this.props.onSearchScopeChanged(SearchScope.DEFAULT);
if (this.queryBar) {
this.queryBar.focusInput();
}
}}
/>
<QueryBar
query=""
onSubmit={this.onSubmit}
onSelect={this.onSelect}
appName="code"
disableAutoFocus={true}
suggestionProviders={this.suggestionProviders}
enableSubmitWhenOptionsChanged={false}
onSearchScopeChanged={this.props.onSearchScopeChanged}
ref={instance => {
if (instance) {
// @ts-ignore
this.queryBar = instance.getWrappedInstance();
}
}}
/>
</div>
);
}
}
export const SearchBar = withRouter(CodeSearchBar);

View file

@ -6,14 +6,13 @@
import { EuiFlexGroup, EuiFlexItem, EuiSelect } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem, EuiSelect } from '@elastic/eui';
import React, { ChangeEvent } from 'react'; import React, { ChangeEvent } from 'react';
import { SearchScope, Repository } from '../../../model'; import { SearchOptions, SearchScope } from '../../../model';
import { ReferenceInfo } from '../../../model/commit'; import { ReferenceInfo } from '../../../model/commit';
import { MainRouteParams } from '../../common/types'; import { MainRouteParams } from '../../common/types';
import { encodeRevisionString } from '../../utils/url'; import { encodeRevisionString } from '../../utils/url';
import { history } from '../../utils/url'; import { history } from '../../utils/url';
import { Breadcrumb } from './breadcrumb'; import { Breadcrumb } from './breadcrumb';
import { SearchBar } from './search_bar'; import { SearchBar } from '../search_bar';
import { SearchOptions } from '../../actions';
interface Props { interface Props {
routeParams: MainRouteParams; routeParams: MainRouteParams;
@ -21,7 +20,7 @@ interface Props {
buttons: React.ReactNode; buttons: React.ReactNode;
searchOptions: SearchOptions; searchOptions: SearchOptions;
branches: ReferenceInfo[]; branches: ReferenceInfo[];
defaultSearchScope?: Repository; query: string;
} }
export class TopBar extends React.Component<Props, { value: string }> { export class TopBar extends React.Component<Props, { value: string }> {
@ -44,8 +43,9 @@ export class TopBar extends React.Component<Props, { value: string }> {
return ( return (
<div className="code-top-bar__container"> <div className="code-top-bar__container">
<SearchBar <SearchBar
defaultSearchScope={this.props.defaultSearchScope} query={this.props.query}
onSearchScopeChanged={this.props.onSearchScopeChanged} onSearchScopeChanged={this.props.onSearchScopeChanged}
enableSubmitWhenOptionsChanged={false}
searchOptions={this.props.searchOptions} searchOptions={this.props.searchOptions}
/> />
<EuiFlexGroup gutterSize="none" justifyContent="spaceBetween"> <EuiFlexGroup gutterSize="none" justifyContent="spaceBetween">

View file

@ -22,8 +22,7 @@ import {
import { EuiIcon } from '@elastic/eui'; import { EuiIcon } from '@elastic/eui';
import { unique } from 'lodash'; import { unique } from 'lodash';
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Repository } from '../../../../model'; import { SearchOptions as ISearchOptions, Repository } from '../../../../model';
import { SearchOptions as ISearchOptions } from '../../../actions';
interface State { interface State {
isFlyoutOpen: boolean; isFlyoutOpen: boolean;

View file

@ -9,15 +9,11 @@ import React, { Component } from 'react';
import { EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiOutsideClickDetector } from '@elastic/eui'; import { EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiOutsideClickDetector } from '@elastic/eui';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { import { saveSearchOptions, searchReposForScope } from '../../../actions';
saveSearchOptions,
SearchOptions as ISearchOptions,
searchReposForScope,
} from '../../../actions';
import { matchPairs } from '../lib/match_pairs'; import { matchPairs } from '../lib/match_pairs';
import { SuggestionsComponent } from './typeahead/suggestions_component'; import { SuggestionsComponent } from './typeahead/suggestions_component';
import { SearchScope, Repository } from '../../../../model'; import { SearchOptions as ISearchOptions, SearchScope, Repository } from '../../../../model';
import { SearchScopePlaceholderText } from '../../../common/types'; import { SearchScopePlaceholderText } from '../../../common/types';
import { RootState } from '../../../reducers'; import { RootState } from '../../../reducers';
import { import {
@ -194,11 +190,15 @@ export class CodeQueryBar extends Component<Props, State> {
const res = await Promise.all( const res = await Promise.all(
this.props.suggestionProviders.map((provider: SuggestionsProvider) => { this.props.suggestionProviders.map((provider: SuggestionsProvider) => {
return provider.getSuggestions( // Merge the default repository scope if necessary.
query, const repoScopes = this.props.searchOptions.repoScope.map(repo => repo.uri);
this.props.searchScope, if (
this.props.searchOptions.repoScope.map(repo => repo.uri) this.props.searchOptions.defaultRepoScopeOn &&
); this.props.searchOptions.defaultRepoScope
) {
repoScopes.push(this.props.searchOptions.defaultRepoScope.uri);
}
return provider.getSuggestions(query, this.props.searchScope, repoScopes);
}) })
); );

View file

@ -8,7 +8,7 @@ import querystring from 'querystring';
import React from 'react'; import React from 'react';
import url from 'url'; import url from 'url';
import { SearchScope } from '../../../model'; import { SearchOptions, SearchScope } from '../../../model';
import { SearchScopeText } from '../../common/types'; import { SearchScopeText } from '../../common/types';
import { history } from '../../utils/url'; import { history } from '../../utils/url';
import { ShortcutsProvider, Shortcut } from '../shortcuts'; import { ShortcutsProvider, Shortcut } from '../shortcuts';
@ -24,13 +24,20 @@ import {
interface Props { interface Props {
query: string; query: string;
onSearchScopeChanged: (s: SearchScope) => void; onSearchScopeChanged: (s: SearchScope) => void;
repoScope: string[]; searchOptions: SearchOptions;
enableSubmitWhenOptionsChanged: boolean;
} }
export class SearchBar extends React.PureComponent<Props> { export class SearchBar extends React.PureComponent<Props> {
public queryBar: any = null; public queryBar: any = null;
public onSearchChanged = (query: string) => { public onSearchChanged = (query: string) => {
// Merge the default repository scope if necessary.
const repoScopes = this.props.searchOptions.repoScope.map(repo => repo.uri);
if (this.props.searchOptions.defaultRepoScopeOn && this.props.searchOptions.defaultRepoScope) {
repoScopes.push(this.props.searchOptions.defaultRepoScope.uri);
}
// Update the url and push to history as well. // Update the url and push to history as well.
const queries = querystring.parse(history.location.search.replace('?', '')); const queries = querystring.parse(history.location.search.replace('?', ''));
history.push( history.push(
@ -39,7 +46,7 @@ export class SearchBar extends React.PureComponent<Props> {
query: { query: {
...queries, ...queries,
q: query, q: query,
repoScope: this.props.repoScope, repoScope: repoScopes,
}, },
}) })
); );
@ -51,21 +58,21 @@ export class SearchBar extends React.PureComponent<Props> {
} }
} }
public onSubmit = (q: string) => {
this.onSearchChanged(q);
};
public onSelect = (item: AutocompleteSuggestion) => {
history.push(item.selectUrl);
};
public suggestionProviders = [
new SymbolSuggestionsProvider(),
new FileSuggestionsProvider(),
new RepositorySuggestionsProvider(),
];
public render() { public render() {
const onSubmit = (q: string) => {
this.onSearchChanged(q);
};
const onSelect = (item: AutocompleteSuggestion) => {
history.push(item.selectUrl);
};
const suggestionProviders = [
new SymbolSuggestionsProvider(),
new FileSuggestionsProvider(),
new RepositorySuggestionsProvider(),
];
return ( return (
<div className="codeSearchbar__container"> <div className="codeSearchbar__container">
<ShortcutsProvider /> <ShortcutsProvider />
@ -101,12 +108,12 @@ export class SearchBar extends React.PureComponent<Props> {
/> />
<QueryBar <QueryBar
query={this.props.query} query={this.props.query}
onSubmit={onSubmit} onSubmit={this.onSubmit}
onSelect={onSelect} onSelect={this.onSelect}
appName="code" appName="code"
suggestionProviders={suggestionProviders} suggestionProviders={this.suggestionProviders}
onSearchScopeChanged={this.props.onSearchScopeChanged} onSearchScopeChanged={this.props.onSearchScopeChanged}
enableSubmitWhenOptionsChanged={true} enableSubmitWhenOptionsChanged={this.props.enableSubmitWhenOptionsChanged}
ref={instance => { ref={instance => {
if (instance) { if (instance) {
// @ts-ignore // @ts-ignore

View file

@ -11,8 +11,8 @@ import { connect } from 'react-redux';
import chrome from 'ui/chrome'; import chrome from 'ui/chrome';
import url from 'url'; import url from 'url';
import { DocumentSearchResult, SearchScope } from '../../../model'; import { DocumentSearchResult, SearchOptions, SearchScope } from '../../../model';
import { changeSearchScope, SearchOptions } from '../../actions'; import { changeSearchScope } from '../../actions';
import { RootState } from '../../reducers'; import { RootState } from '../../reducers';
import { history } from '../../utils/url'; import { history } from '../../utils/url';
import { ProjectItem } from '../admin_page/project_item'; import { ProjectItem } from '../admin_page/project_item';
@ -209,9 +209,10 @@ class SearchPage extends React.PureComponent<Props, State> {
/> />
<div className="codeContainer__search--main"> <div className="codeContainer__search--main">
<SearchBar <SearchBar
repoScope={this.props.searchOptions.repoScope.map(r => r.uri)} searchOptions={this.props.searchOptions}
query={this.props.query} query={this.props.query}
onSearchScopeChanged={this.props.onSearchScopeChanged} onSearchScopeChanged={this.props.onSearchScopeChanged}
enableSubmitWhenOptionsChanged={true}
ref={(element: any) => (this.searchBar = element)} ref={(element: any) => (this.searchBar = element)}
/> />
{mainComp} {mainComp}

View file

@ -8,7 +8,7 @@ import produce from 'immer';
import { Action, handleActions } from 'redux-actions'; import { Action, handleActions } from 'redux-actions';
import { DocumentSearchResult, RepositoryUri, SearchScope } from '../../model'; import { DocumentSearchResult, RepositoryUri, SearchOptions, SearchScope } from '../../model';
import { import {
changeSearchScope, changeSearchScope,
documentSearch as documentSearchQuery, documentSearch as documentSearchQuery,
@ -20,7 +20,6 @@ import {
RepositorySearchPayload, RepositorySearchPayload,
repositorySearchSuccess, repositorySearchSuccess,
saveSearchOptions, saveSearchOptions,
SearchOptions,
searchReposForScope, searchReposForScope,
searchReposForScopeSuccess, searchReposForScopeSuccess,
turnOnDefaultRepoScope, turnOnDefaultRepoScope,
@ -177,6 +176,7 @@ export const search = handleActions<SearchState, any>(
}), }),
[String(turnOnDefaultRepoScope)]: (state: SearchState, action: Action<any>) => [String(turnOnDefaultRepoScope)]: (state: SearchState, action: Action<any>) =>
produce<SearchState>(state, draft => { produce<SearchState>(state, draft => {
draft.searchOptions.defaultRepoScope = action.payload;
draft.searchOptions.defaultRepoScopeOn = true; draft.searchOptions.defaultRepoScopeOn = true;
}), }),
}, },

View file

@ -136,6 +136,12 @@ function* loadRepoSaga(action: any) {
try { try {
const repo = yield call(fetchRepo, action.payload); const repo = yield call(fetchRepo, action.payload);
yield put(loadRepoSuccess(repo)); yield put(loadRepoSuccess(repo));
// turn on defaultRepoScope if there's no repo scope specified when enter a source view page
const repoScope = yield select(repoScopeSelector);
if (repoScope.length === 0) {
yield put(turnOnDefaultRepoScope(repo));
}
} catch (e) { } catch (e) {
yield put(loadRepoFailed(e)); yield put(loadRepoFailed(e));
} }
@ -148,11 +154,7 @@ export function* watchLoadRepo() {
function* handleMainRouteChange(action: Action<Match>) { function* handleMainRouteChange(action: Action<Match>) {
// in source view page, we need repos as default repo scope options when no query input // in source view page, we need repos as default repo scope options when no query input
yield put(fetchRepos()); yield put(fetchRepos());
// turn on defaultRepoScope if there's no repo scope specified when enter a source view page
const repoScope = yield select(repoScopeSelector);
if (repoScope.length === 0) {
yield put(turnOnDefaultRepoScope());
}
const { location } = action.payload!; const { location } = action.payload!;
const search = location.search.startsWith('?') ? location.search.substring(1) : location.search; const search = location.search.startsWith('?') ? location.search.substring(1) : location.search;
const queryParams = queryString.parse(search); const queryParams = queryString.parse(search);