remove search code
This commit is contained in:
parent
a1d5ce7e7e
commit
04e3ca8250
11 changed files with 247 additions and 1873 deletions
1126
dist/bundle.es.js
vendored
1126
dist/bundle.es.js
vendored
File diff suppressed because it is too large
Load diff
84
dist/flow-typed/Search.js
vendored
84
dist/flow-typed/Search.js
vendored
|
@ -1,84 +0,0 @@
|
||||||
// @flow
|
|
||||||
import * as ACTIONS from 'constants/action_types';
|
|
||||||
|
|
||||||
declare type SearchSuggestion = {
|
|
||||||
value: string,
|
|
||||||
shorthand: string,
|
|
||||||
type: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type SearchOptions = {
|
|
||||||
// :(
|
|
||||||
// https://github.com/facebook/flow/issues/6492
|
|
||||||
RESULT_COUNT: number,
|
|
||||||
CLAIM_TYPE: string,
|
|
||||||
INCLUDE_FILES: string,
|
|
||||||
INCLUDE_CHANNELS: string,
|
|
||||||
INCLUDE_FILES_AND_CHANNELS: string,
|
|
||||||
MEDIA_AUDIO: string,
|
|
||||||
MEDIA_VIDEO: string,
|
|
||||||
MEDIA_TEXT: string,
|
|
||||||
MEDIA_IMAGE: string,
|
|
||||||
MEDIA_APPLICATION: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type SearchState = {
|
|
||||||
isActive: boolean,
|
|
||||||
searchQuery: string,
|
|
||||||
options: SearchOptions,
|
|
||||||
suggestions: { [string]: Array<SearchSuggestion> },
|
|
||||||
urisByQuery: {},
|
|
||||||
resolvedResultsByQuery: {},
|
|
||||||
resolvedResultsByQueryLastPageReached: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type SearchSuccess = {
|
|
||||||
type: ACTIONS.SEARCH_SUCCESS,
|
|
||||||
data: {
|
|
||||||
query: string,
|
|
||||||
uris: Array<string>,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type UpdateSearchQuery = {
|
|
||||||
type: ACTIONS.UPDATE_SEARCH_QUERY,
|
|
||||||
data: {
|
|
||||||
query: string,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type UpdateSearchSuggestions = {
|
|
||||||
type: ACTIONS.UPDATE_SEARCH_SUGGESTIONS,
|
|
||||||
data: {
|
|
||||||
query: string,
|
|
||||||
suggestions: Array<SearchSuggestion>,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type UpdateSearchOptions = {
|
|
||||||
type: ACTIONS.UPDATE_SEARCH_OPTIONS,
|
|
||||||
data: SearchOptions,
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type ResolvedSearchResult = {
|
|
||||||
channel: string,
|
|
||||||
channel_claim_id: string,
|
|
||||||
claimId: string,
|
|
||||||
duration: number,
|
|
||||||
fee: number,
|
|
||||||
name: string,
|
|
||||||
nsfw: boolean,
|
|
||||||
release_time: string,
|
|
||||||
thumbnail_url: string,
|
|
||||||
title: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type ResolvedSearchSuccess = {
|
|
||||||
type: ACTIONS.RESOLVED_SEARCH_SUCCESS,
|
|
||||||
data: {
|
|
||||||
append: boolean,
|
|
||||||
pageSize: number,
|
|
||||||
results: Array<ResolvedSearchResult>,
|
|
||||||
query: string,
|
|
||||||
},
|
|
||||||
};
|
|
84
flow-typed/Search.js
vendored
84
flow-typed/Search.js
vendored
|
@ -1,84 +0,0 @@
|
||||||
// @flow
|
|
||||||
import * as ACTIONS from 'constants/action_types';
|
|
||||||
|
|
||||||
declare type SearchSuggestion = {
|
|
||||||
value: string,
|
|
||||||
shorthand: string,
|
|
||||||
type: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type SearchOptions = {
|
|
||||||
// :(
|
|
||||||
// https://github.com/facebook/flow/issues/6492
|
|
||||||
RESULT_COUNT: number,
|
|
||||||
CLAIM_TYPE: string,
|
|
||||||
INCLUDE_FILES: string,
|
|
||||||
INCLUDE_CHANNELS: string,
|
|
||||||
INCLUDE_FILES_AND_CHANNELS: string,
|
|
||||||
MEDIA_AUDIO: string,
|
|
||||||
MEDIA_VIDEO: string,
|
|
||||||
MEDIA_TEXT: string,
|
|
||||||
MEDIA_IMAGE: string,
|
|
||||||
MEDIA_APPLICATION: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type SearchState = {
|
|
||||||
isActive: boolean,
|
|
||||||
searchQuery: string,
|
|
||||||
options: SearchOptions,
|
|
||||||
suggestions: { [string]: Array<SearchSuggestion> },
|
|
||||||
urisByQuery: {},
|
|
||||||
resolvedResultsByQuery: {},
|
|
||||||
resolvedResultsByQueryLastPageReached: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type SearchSuccess = {
|
|
||||||
type: ACTIONS.SEARCH_SUCCESS,
|
|
||||||
data: {
|
|
||||||
query: string,
|
|
||||||
uris: Array<string>,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type UpdateSearchQuery = {
|
|
||||||
type: ACTIONS.UPDATE_SEARCH_QUERY,
|
|
||||||
data: {
|
|
||||||
query: string,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type UpdateSearchSuggestions = {
|
|
||||||
type: ACTIONS.UPDATE_SEARCH_SUGGESTIONS,
|
|
||||||
data: {
|
|
||||||
query: string,
|
|
||||||
suggestions: Array<SearchSuggestion>,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type UpdateSearchOptions = {
|
|
||||||
type: ACTIONS.UPDATE_SEARCH_OPTIONS,
|
|
||||||
data: SearchOptions,
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type ResolvedSearchResult = {
|
|
||||||
channel: string,
|
|
||||||
channel_claim_id: string,
|
|
||||||
claimId: string,
|
|
||||||
duration: number,
|
|
||||||
fee: number,
|
|
||||||
name: string,
|
|
||||||
nsfw: boolean,
|
|
||||||
release_time: string,
|
|
||||||
thumbnail_url: string,
|
|
||||||
title: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
declare type ResolvedSearchSuccess = {
|
|
||||||
type: ACTIONS.RESOLVED_SEARCH_SUCCESS,
|
|
||||||
data: {
|
|
||||||
append: boolean,
|
|
||||||
pageSize: number,
|
|
||||||
results: Array<ResolvedSearchResult>,
|
|
||||||
query: string,
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -172,19 +172,6 @@ export const PURCHASE_URI_COMPLETED = 'PURCHASE_URI_COMPLETED';
|
||||||
export const PURCHASE_URI_FAILED = 'PURCHASE_URI_FAILED';
|
export const PURCHASE_URI_FAILED = 'PURCHASE_URI_FAILED';
|
||||||
export const CLEAR_PURCHASED_URI_SUCCESS = 'CLEAR_PURCHASED_URI_SUCCESS';
|
export const CLEAR_PURCHASED_URI_SUCCESS = 'CLEAR_PURCHASED_URI_SUCCESS';
|
||||||
|
|
||||||
// Search
|
|
||||||
export const SEARCH_START = 'SEARCH_START';
|
|
||||||
export const SEARCH_SUCCESS = 'SEARCH_SUCCESS';
|
|
||||||
export const SEARCH_FAIL = 'SEARCH_FAIL';
|
|
||||||
export const RESOLVED_SEARCH_START = 'RESOLVED_SEARCH_START';
|
|
||||||
export const RESOLVED_SEARCH_SUCCESS = 'RESOLVED_SEARCH_SUCCESS';
|
|
||||||
export const RESOLVED_SEARCH_FAIL = 'RESOLVED_SEARCH_FAIL';
|
|
||||||
export const UPDATE_SEARCH_QUERY = 'UPDATE_SEARCH_QUERY';
|
|
||||||
export const UPDATE_SEARCH_OPTIONS = 'UPDATE_SEARCH_OPTIONS';
|
|
||||||
export const UPDATE_SEARCH_SUGGESTIONS = 'UPDATE_SEARCH_SUGGESTIONS';
|
|
||||||
export const SEARCH_FOCUS = 'SEARCH_FOCUS';
|
|
||||||
export const SEARCH_BLUR = 'SEARCH_BLUR';
|
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
export const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED';
|
export const DAEMON_SETTINGS_RECEIVED = 'DAEMON_SETTINGS_RECEIVED';
|
||||||
export const DAEMON_STATUS_RECEIVED = 'DAEMON_STATUS_RECEIVED';
|
export const DAEMON_STATUS_RECEIVED = 'DAEMON_STATUS_RECEIVED';
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
export const SEARCH_TYPES = {
|
|
||||||
FILE: 'file',
|
|
||||||
CHANNEL: 'channel',
|
|
||||||
SEARCH: 'search',
|
|
||||||
TAG: 'tag',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SEARCH_OPTIONS = {
|
|
||||||
RESULT_COUNT: 'size',
|
|
||||||
CLAIM_TYPE: 'claimType',
|
|
||||||
INCLUDE_FILES: 'file',
|
|
||||||
INCLUDE_CHANNELS: 'channel',
|
|
||||||
INCLUDE_FILES_AND_CHANNELS: 'file,channel',
|
|
||||||
MEDIA_AUDIO: 'audio',
|
|
||||||
MEDIA_VIDEO: 'video',
|
|
||||||
MEDIA_TEXT: 'text',
|
|
||||||
MEDIA_IMAGE: 'image',
|
|
||||||
MEDIA_APPLICATION: 'application',
|
|
||||||
};
|
|
34
src/index.js
34
src/index.js
|
@ -12,11 +12,9 @@ import * as TXO_LIST from 'constants/txo_list';
|
||||||
import * as SPEECH_URLS from 'constants/speech_urls';
|
import * as SPEECH_URLS from 'constants/speech_urls';
|
||||||
import * as DAEMON_SETTINGS from 'constants/daemon_settings';
|
import * as DAEMON_SETTINGS from 'constants/daemon_settings';
|
||||||
import * as SHARED_PREFERENCES from 'constants/shared_preferences';
|
import * as SHARED_PREFERENCES from 'constants/shared_preferences';
|
||||||
import { SEARCH_TYPES, SEARCH_OPTIONS } from 'constants/search';
|
|
||||||
import { DEFAULT_KNOWN_TAGS, DEFAULT_FOLLOWED_TAGS, MATURE_TAGS } from 'constants/tags';
|
import { DEFAULT_KNOWN_TAGS, DEFAULT_FOLLOWED_TAGS, MATURE_TAGS } from 'constants/tags';
|
||||||
import Lbry, { apiCall } from 'lbry';
|
import Lbry, { apiCall } from 'lbry';
|
||||||
import LbryFirst from 'lbry-first';
|
import LbryFirst from 'lbry-first';
|
||||||
import { selectState as selectSearchState } from 'redux/selectors/search';
|
|
||||||
|
|
||||||
// constants
|
// constants
|
||||||
export {
|
export {
|
||||||
|
@ -24,8 +22,6 @@ export {
|
||||||
CLAIM_VALUES,
|
CLAIM_VALUES,
|
||||||
LICENSES,
|
LICENSES,
|
||||||
THUMBNAIL_STATUSES,
|
THUMBNAIL_STATUSES,
|
||||||
SEARCH_TYPES,
|
|
||||||
SEARCH_OPTIONS,
|
|
||||||
SETTINGS,
|
SETTINGS,
|
||||||
DAEMON_SETTINGS,
|
DAEMON_SETTINGS,
|
||||||
TRANSACTIONS,
|
TRANSACTIONS,
|
||||||
|
@ -101,16 +97,6 @@ export {
|
||||||
doCheckReflectingFiles,
|
doCheckReflectingFiles,
|
||||||
} from 'redux/actions/publish';
|
} from 'redux/actions/publish';
|
||||||
|
|
||||||
export {
|
|
||||||
doSearch,
|
|
||||||
doResolvedSearch,
|
|
||||||
doUpdateSearchQuery,
|
|
||||||
doFocusSearchInput,
|
|
||||||
doBlurSearchInput,
|
|
||||||
setSearchApi,
|
|
||||||
doUpdateSearchOptions,
|
|
||||||
} from 'redux/actions/search';
|
|
||||||
|
|
||||||
export { savePosition } from 'redux/actions/content';
|
export { savePosition } from 'redux/actions/content';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -152,7 +138,6 @@ export { contentReducer } from 'redux/reducers/content';
|
||||||
export { fileInfoReducer } from 'redux/reducers/file_info';
|
export { fileInfoReducer } from 'redux/reducers/file_info';
|
||||||
export { notificationsReducer } from 'redux/reducers/notifications';
|
export { notificationsReducer } from 'redux/reducers/notifications';
|
||||||
export { publishReducer } from 'redux/reducers/publish';
|
export { publishReducer } from 'redux/reducers/publish';
|
||||||
export { searchReducer } from 'redux/reducers/search';
|
|
||||||
export { tagsReducer } from 'redux/reducers/tags';
|
export { tagsReducer } from 'redux/reducers/tags';
|
||||||
export { walletReducer } from 'redux/reducers/wallet';
|
export { walletReducer } from 'redux/reducers/wallet';
|
||||||
|
|
||||||
|
@ -184,9 +169,6 @@ export {
|
||||||
makeSelectNsfwCountForChannel,
|
makeSelectNsfwCountForChannel,
|
||||||
makeSelectOmittedCountForChannel,
|
makeSelectOmittedCountForChannel,
|
||||||
makeSelectClaimIsNsfw,
|
makeSelectClaimIsNsfw,
|
||||||
makeSelectRecommendedContentForUri,
|
|
||||||
makeSelectResolvedRecommendedContentForUri,
|
|
||||||
makeSelectFirstRecommendedFileForUri,
|
|
||||||
makeSelectChannelForClaimUri,
|
makeSelectChannelForClaimUri,
|
||||||
makeSelectClaimIsPending,
|
makeSelectClaimIsPending,
|
||||||
makeSelectReflectingClaimForUri,
|
makeSelectReflectingClaimForUri,
|
||||||
|
@ -280,22 +262,6 @@ export {
|
||||||
selectTakeOverAmount,
|
selectTakeOverAmount,
|
||||||
} from 'redux/selectors/publish';
|
} from 'redux/selectors/publish';
|
||||||
|
|
||||||
export { selectSearchState };
|
|
||||||
export {
|
|
||||||
makeSelectSearchUris,
|
|
||||||
makeSelectResolvedSearchResults,
|
|
||||||
makeSelectResolvedSearchResultsLastPageReached,
|
|
||||||
selectSearchValue,
|
|
||||||
selectSearchOptions,
|
|
||||||
selectIsSearching,
|
|
||||||
selectResolvedSearchResultsByQuery,
|
|
||||||
selectResolvedSearchResultsByQueryLastPageReached,
|
|
||||||
selectSearchUrisByQuery,
|
|
||||||
selectSearchBarFocused,
|
|
||||||
selectSearchSuggestions,
|
|
||||||
makeSelectQueryWithOptions,
|
|
||||||
} from 'redux/selectors/search';
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
selectBalance,
|
selectBalance,
|
||||||
selectTotalBalance,
|
selectTotalBalance,
|
||||||
|
|
|
@ -1,278 +0,0 @@
|
||||||
// @flow
|
|
||||||
import * as ACTIONS from 'constants/action_types';
|
|
||||||
import { buildURI } from 'lbryURI';
|
|
||||||
import { doResolveUri } from 'redux/actions/claims';
|
|
||||||
import {
|
|
||||||
makeSelectSearchUris,
|
|
||||||
makeSelectResolvedSearchResults,
|
|
||||||
selectSuggestions,
|
|
||||||
makeSelectQueryWithOptions,
|
|
||||||
selectSearchValue,
|
|
||||||
} from 'redux/selectors/search';
|
|
||||||
import { batchActions } from 'util/batch-actions';
|
|
||||||
import debounce from 'util/debounce';
|
|
||||||
import handleFetchResponse from 'util/handle-fetch';
|
|
||||||
|
|
||||||
const DEBOUNCED_SEARCH_SUGGESTION_MS = 300;
|
|
||||||
type Dispatch = (action: any) => any;
|
|
||||||
type GetState = () => { search: SearchState };
|
|
||||||
|
|
||||||
type SearchOptions = {
|
|
||||||
size?: number,
|
|
||||||
from?: number,
|
|
||||||
related_to?: string,
|
|
||||||
nsfw?: boolean,
|
|
||||||
isBackgroundSearch?: boolean,
|
|
||||||
resolveResults?: boolean,
|
|
||||||
};
|
|
||||||
|
|
||||||
// We can't use env's because they aren't passed into node_modules
|
|
||||||
let CONNECTION_STRING = 'https://lighthouse.lbry.com/';
|
|
||||||
|
|
||||||
export const setSearchApi = (endpoint: string) => {
|
|
||||||
CONNECTION_STRING = endpoint.replace(/\/*$/, '/'); // exactly one slash at the end;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getSearchSuggestions = (value: string) => (dispatch: Dispatch, getState: GetState) => {
|
|
||||||
const query = value.trim();
|
|
||||||
|
|
||||||
// strip out any basic stuff for more accurate search results
|
|
||||||
let searchValue = query.replace(/lbry:\/\//g, '').replace(/-/g, ' ');
|
|
||||||
if (searchValue.includes('#')) {
|
|
||||||
// This should probably be more robust, but I think it's fine for now
|
|
||||||
// Remove everything after # to get rid of the claim id
|
|
||||||
searchValue = searchValue.substring(0, searchValue.indexOf('#'));
|
|
||||||
}
|
|
||||||
|
|
||||||
const suggestions = selectSuggestions(getState());
|
|
||||||
if (suggestions[searchValue]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(`${CONNECTION_STRING}autocomplete?s=${searchValue}`)
|
|
||||||
.then(handleFetchResponse)
|
|
||||||
.then(apiSuggestions => {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.UPDATE_SEARCH_SUGGESTIONS,
|
|
||||||
data: {
|
|
||||||
query: searchValue,
|
|
||||||
suggestions: apiSuggestions,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// If the fetch fails, do nothing
|
|
||||||
// Basic search suggestions are already populated at this point
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const throttledSearchSuggestions = debounce((dispatch, query) => {
|
|
||||||
dispatch(getSearchSuggestions(query));
|
|
||||||
}, DEBOUNCED_SEARCH_SUGGESTION_MS);
|
|
||||||
|
|
||||||
export const doUpdateSearchQuery = (query: string, shouldSkipSuggestions: ?boolean) => (
|
|
||||||
dispatch: Dispatch
|
|
||||||
) => {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.UPDATE_SEARCH_QUERY,
|
|
||||||
data: { query },
|
|
||||||
});
|
|
||||||
|
|
||||||
// Don't fetch new suggestions if the user just added a space
|
|
||||||
if (!query.endsWith(' ') || !shouldSkipSuggestions) {
|
|
||||||
throttledSearchSuggestions(dispatch, query);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const doSearch = (rawQuery: string, searchOptions: SearchOptions) => (
|
|
||||||
dispatch: Dispatch,
|
|
||||||
getState: GetState
|
|
||||||
) => {
|
|
||||||
const query = rawQuery.replace(/^lbry:\/\//i, '').replace(/\//, ' ');
|
|
||||||
const resolveResults = searchOptions && searchOptions.resolveResults;
|
|
||||||
const isBackgroundSearch = (searchOptions && searchOptions.isBackgroundSearch) || false;
|
|
||||||
|
|
||||||
if (!query) {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.SEARCH_FAIL,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const state = getState();
|
|
||||||
|
|
||||||
let queryWithOptions = makeSelectQueryWithOptions(query, searchOptions)(state);
|
|
||||||
|
|
||||||
// If we have already searched for something, we don't need to do anything
|
|
||||||
const urisForQuery = makeSelectSearchUris(queryWithOptions)(state);
|
|
||||||
if (urisForQuery && !!urisForQuery.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.SEARCH_START,
|
|
||||||
});
|
|
||||||
|
|
||||||
// If the user is on the file page with a pre-populated uri and they select
|
|
||||||
// the search option without typing anything, searchQuery will be empty
|
|
||||||
// We need to populate it so the input is filled on the search page
|
|
||||||
// isBackgroundSearch means the search is happening in the background, don't update the search query
|
|
||||||
if (!state.search.searchQuery && !isBackgroundSearch) {
|
|
||||||
dispatch(doUpdateSearchQuery(query));
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch(`${CONNECTION_STRING}search?${queryWithOptions}`)
|
|
||||||
.then(handleFetchResponse)
|
|
||||||
.then((data: Array<{ name: string, claimId: string }>) => {
|
|
||||||
const uris = [];
|
|
||||||
const actions = [];
|
|
||||||
|
|
||||||
data.forEach(result => {
|
|
||||||
if (result) {
|
|
||||||
const { name, claimId } = result;
|
|
||||||
const urlObj: LbryUrlObj = {};
|
|
||||||
|
|
||||||
if (name.startsWith('@')) {
|
|
||||||
urlObj.channelName = name;
|
|
||||||
urlObj.channelClaimId = claimId;
|
|
||||||
} else {
|
|
||||||
urlObj.streamName = name;
|
|
||||||
urlObj.streamClaimId = claimId;
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = buildURI(urlObj);
|
|
||||||
if (resolveResults) {
|
|
||||||
actions.push(doResolveUri(url));
|
|
||||||
}
|
|
||||||
uris.push(url);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
actions.push({
|
|
||||||
type: ACTIONS.SEARCH_SUCCESS,
|
|
||||||
data: {
|
|
||||||
query: queryWithOptions,
|
|
||||||
uris,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
dispatch(batchActions(...actions));
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.SEARCH_FAIL,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const doResolvedSearch = (
|
|
||||||
rawQuery: string,
|
|
||||||
size: ?number, // only pass in if you don't want to use the users setting (ex: related content)
|
|
||||||
from: ?number,
|
|
||||||
isBackgroundSearch: boolean = false,
|
|
||||||
options: {
|
|
||||||
related_to?: string,
|
|
||||||
} = {},
|
|
||||||
nsfw: boolean
|
|
||||||
) => (dispatch: Dispatch, getState: GetState) => {
|
|
||||||
const query = rawQuery.replace(/^lbry:\/\//i, '').replace(/\//, ' ');
|
|
||||||
|
|
||||||
if (!query) {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.RESOLVED_SEARCH_FAIL,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const optionsWithFrom: SearchOptions = {
|
|
||||||
...(size ? { size } : {}),
|
|
||||||
...(from ? { from } : {}),
|
|
||||||
isBackgroundSearch,
|
|
||||||
...options,
|
|
||||||
};
|
|
||||||
|
|
||||||
const optionsWithoutFrom: SearchOptions = {
|
|
||||||
...(size ? { size } : {}),
|
|
||||||
isBackgroundSearch,
|
|
||||||
...options,
|
|
||||||
};
|
|
||||||
|
|
||||||
const state = getState();
|
|
||||||
|
|
||||||
let queryWithOptions = makeSelectQueryWithOptions(query, optionsWithFrom)(state);
|
|
||||||
|
|
||||||
// make from null so that we can maintain a reference to the same query for multiple pages and simply append the found results
|
|
||||||
let queryWithoutFrom = makeSelectQueryWithOptions(query, optionsWithoutFrom)(state);
|
|
||||||
|
|
||||||
// If we have already searched for something, we don't need to do anything
|
|
||||||
// TODO: Tweak this check for multiple page results
|
|
||||||
/* const resultsForQuery = makeSelectResolvedSearchResults(queryWithOptions)(state);
|
|
||||||
if (resultsForQuery && resultsForQuery.length && resultsForQuery.length > (from * size)) {
|
|
||||||
return;
|
|
||||||
} */
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.RESOLVED_SEARCH_START,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!state.search.searchQuery && !isBackgroundSearch) {
|
|
||||||
dispatch(doUpdateSearchQuery(query));
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchUrl = nsfw
|
|
||||||
? `${CONNECTION_STRING}search?resolve=true&${queryWithOptions}`
|
|
||||||
: `${CONNECTION_STRING}search?resolve=true&nsfw=false&${queryWithOptions}`;
|
|
||||||
fetch(fetchUrl)
|
|
||||||
.then(handleFetchResponse)
|
|
||||||
.then((data: Array<ResolvedSearchResult>) => {
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
data.forEach(result => {
|
|
||||||
if (result) {
|
|
||||||
results.push(result);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.RESOLVED_SEARCH_SUCCESS,
|
|
||||||
data: {
|
|
||||||
query: queryWithoutFrom,
|
|
||||||
results,
|
|
||||||
pageSize: size,
|
|
||||||
append: parseInt(from, 10) > parseInt(size, 10) - 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.RESOLVED_SEARCH_FAIL,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const doFocusSearchInput = () => (dispatch: Dispatch) =>
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.SEARCH_FOCUS,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const doBlurSearchInput = () => (dispatch: Dispatch) =>
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.SEARCH_BLUR,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const doUpdateSearchOptions = (
|
|
||||||
newOptions: SearchOptions,
|
|
||||||
additionalOptions: SearchOptions
|
|
||||||
) => (dispatch: Dispatch, getState: GetState) => {
|
|
||||||
const state = getState();
|
|
||||||
const searchValue = selectSearchValue(state);
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.UPDATE_SEARCH_OPTIONS,
|
|
||||||
data: newOptions,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (searchValue) {
|
|
||||||
// After updating, perform a search with the new options
|
|
||||||
dispatch(doSearch(searchValue, additionalOptions));
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,137 +0,0 @@
|
||||||
// @flow
|
|
||||||
import * as ACTIONS from 'constants/action_types';
|
|
||||||
import { handleActions } from 'util/redux-utils';
|
|
||||||
import { SEARCH_OPTIONS } from 'constants/search';
|
|
||||||
|
|
||||||
const defaultState = {
|
|
||||||
isActive: false, // does the user have any typed text in the search input
|
|
||||||
focused: false, // is the search input focused
|
|
||||||
searchQuery: '', // needs to be an empty string for input focusing
|
|
||||||
options: {
|
|
||||||
[SEARCH_OPTIONS.RESULT_COUNT]: 30,
|
|
||||||
[SEARCH_OPTIONS.CLAIM_TYPE]: SEARCH_OPTIONS.INCLUDE_FILES_AND_CHANNELS,
|
|
||||||
[SEARCH_OPTIONS.MEDIA_AUDIO]: true,
|
|
||||||
[SEARCH_OPTIONS.MEDIA_VIDEO]: true,
|
|
||||||
[SEARCH_OPTIONS.MEDIA_TEXT]: true,
|
|
||||||
[SEARCH_OPTIONS.MEDIA_IMAGE]: true,
|
|
||||||
[SEARCH_OPTIONS.MEDIA_APPLICATION]: true,
|
|
||||||
},
|
|
||||||
suggestions: {},
|
|
||||||
urisByQuery: {},
|
|
||||||
resolvedResultsByQuery: {},
|
|
||||||
resolvedResultsByQueryLastPageReached: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const searchReducer = handleActions(
|
|
||||||
{
|
|
||||||
[ACTIONS.SEARCH_START]: (state: SearchState): SearchState => ({
|
|
||||||
...state,
|
|
||||||
searching: true,
|
|
||||||
}),
|
|
||||||
[ACTIONS.SEARCH_SUCCESS]: (state: SearchState, action: SearchSuccess): SearchState => {
|
|
||||||
const { query, uris } = action.data;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
searching: false,
|
|
||||||
urisByQuery: Object.assign({}, state.urisByQuery, { [query]: uris }),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
[ACTIONS.SEARCH_FAIL]: (state: SearchState): SearchState => ({
|
|
||||||
...state,
|
|
||||||
searching: false,
|
|
||||||
}),
|
|
||||||
|
|
||||||
[ACTIONS.RESOLVED_SEARCH_START]: (state: SearchState): SearchState => ({
|
|
||||||
...state,
|
|
||||||
searching: true,
|
|
||||||
}),
|
|
||||||
[ACTIONS.RESOLVED_SEARCH_SUCCESS]: (
|
|
||||||
state: SearchState,
|
|
||||||
action: ResolvedSearchSuccess
|
|
||||||
): SearchState => {
|
|
||||||
const resolvedResultsByQuery = Object.assign({}, state.resolvedResultsByQuery);
|
|
||||||
const resolvedResultsByQueryLastPageReached = Object.assign(
|
|
||||||
{},
|
|
||||||
state.resolvedResultsByQueryLastPageReached
|
|
||||||
);
|
|
||||||
const { append, query, results, pageSize } = action.data;
|
|
||||||
|
|
||||||
if (append) {
|
|
||||||
// todo: check for duplicates when concatenating?
|
|
||||||
resolvedResultsByQuery[query] =
|
|
||||||
resolvedResultsByQuery[query] && resolvedResultsByQuery[query].length
|
|
||||||
? resolvedResultsByQuery[query].concat(results)
|
|
||||||
: results;
|
|
||||||
} else {
|
|
||||||
resolvedResultsByQuery[query] = results;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the returned number of urls is less than the page size, so we're on the last page
|
|
||||||
resolvedResultsByQueryLastPageReached[query] = results.length < pageSize;
|
|
||||||
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
searching: false,
|
|
||||||
resolvedResultsByQuery,
|
|
||||||
resolvedResultsByQueryLastPageReached,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
[ACTIONS.RESOLVED_SEARCH_FAIL]: (state: SearchState): SearchState => ({
|
|
||||||
...state,
|
|
||||||
searching: false,
|
|
||||||
}),
|
|
||||||
|
|
||||||
[ACTIONS.UPDATE_SEARCH_QUERY]: (
|
|
||||||
state: SearchState,
|
|
||||||
action: UpdateSearchQuery
|
|
||||||
): SearchState => ({
|
|
||||||
...state,
|
|
||||||
searchQuery: action.data.query,
|
|
||||||
isActive: true,
|
|
||||||
}),
|
|
||||||
|
|
||||||
[ACTIONS.UPDATE_SEARCH_SUGGESTIONS]: (
|
|
||||||
state: SearchState,
|
|
||||||
action: UpdateSearchSuggestions
|
|
||||||
): SearchState => ({
|
|
||||||
...state,
|
|
||||||
suggestions: {
|
|
||||||
...state.suggestions,
|
|
||||||
[action.data.query]: action.data.suggestions,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
// sets isActive to false so the uri will be populated correctly if the
|
|
||||||
// user is on a file page. The search query will still be present on any
|
|
||||||
// other page
|
|
||||||
[ACTIONS.DISMISS_NOTIFICATION]: (state: SearchState): SearchState => ({
|
|
||||||
...state,
|
|
||||||
isActive: false,
|
|
||||||
}),
|
|
||||||
|
|
||||||
[ACTIONS.SEARCH_FOCUS]: (state: SearchState): SearchState => ({
|
|
||||||
...state,
|
|
||||||
focused: true,
|
|
||||||
}),
|
|
||||||
[ACTIONS.SEARCH_BLUR]: (state: SearchState): SearchState => ({
|
|
||||||
...state,
|
|
||||||
focused: false,
|
|
||||||
}),
|
|
||||||
[ACTIONS.UPDATE_SEARCH_OPTIONS]: (
|
|
||||||
state: SearchState,
|
|
||||||
action: UpdateSearchOptions
|
|
||||||
): SearchState => {
|
|
||||||
const { options: oldOptions } = state;
|
|
||||||
const newOptions = action.data;
|
|
||||||
const options = { ...oldOptions, ...newOptions };
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
options,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultState
|
|
||||||
);
|
|
|
@ -1,13 +1,8 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { normalizeURI, buildURI, parseURI } from 'lbryURI';
|
import { normalizeURI, buildURI, parseURI } from 'lbryURI';
|
||||||
import {
|
|
||||||
selectResolvedSearchResultsByQuery,
|
|
||||||
selectSearchUrisByQuery,
|
|
||||||
} from 'redux/selectors/search';
|
|
||||||
import { selectSupportsByOutpoint } from 'redux/selectors/wallet';
|
import { selectSupportsByOutpoint } from 'redux/selectors/wallet';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { isClaimNsfw, filterClaims } from 'util/claim';
|
import { isClaimNsfw, filterClaims } from 'util/claim';
|
||||||
import { getSearchQueryString } from 'util/query-params';
|
|
||||||
import { PAGE_SIZE } from 'constants/claim';
|
import { PAGE_SIZE } from 'constants/claim';
|
||||||
|
|
||||||
const selectState = state => state.claims || {};
|
const selectState = state => state.claims || {};
|
||||||
|
@ -632,53 +627,6 @@ export const makeSelectClaimIsNsfw = (uri: string): boolean =>
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
export const makeSelectRecommendedContentForUri = (uri: string) =>
|
|
||||||
createSelector(
|
|
||||||
makeSelectClaimForUri(uri),
|
|
||||||
selectSearchUrisByQuery,
|
|
||||||
makeSelectClaimIsNsfw(uri),
|
|
||||||
(claim, searchUrisByQuery, isMature) => {
|
|
||||||
const atVanityURI = !uri.includes('#');
|
|
||||||
|
|
||||||
let recommendedContent;
|
|
||||||
if (claim) {
|
|
||||||
// always grab full URL - this can change once search returns canonical
|
|
||||||
const currentUri = buildURI({ streamClaimId: claim.claim_id, streamName: claim.name });
|
|
||||||
|
|
||||||
const { title } = claim.value;
|
|
||||||
|
|
||||||
if (!title) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const options: {
|
|
||||||
related_to?: string,
|
|
||||||
nsfw?: boolean,
|
|
||||||
isBackgroundSearch?: boolean,
|
|
||||||
} = { related_to: claim.claim_id, isBackgroundSearch: true };
|
|
||||||
|
|
||||||
if (!isMature) {
|
|
||||||
options['nsfw'] = false;
|
|
||||||
}
|
|
||||||
const searchQuery = getSearchQueryString(title.replace(/\//, ' '), options);
|
|
||||||
|
|
||||||
let searchUris = searchUrisByQuery[searchQuery];
|
|
||||||
if (searchUris) {
|
|
||||||
searchUris = searchUris.filter(searchUri => searchUri !== currentUri);
|
|
||||||
recommendedContent = searchUris;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return recommendedContent;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
export const makeSelectFirstRecommendedFileForUri = (uri: string) =>
|
|
||||||
createSelector(
|
|
||||||
makeSelectRecommendedContentForUri(uri),
|
|
||||||
recommendedContent => (recommendedContent ? recommendedContent[0] : null)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Returns the associated channel uri for a given claim uri
|
// Returns the associated channel uri for a given claim uri
|
||||||
// accepts a regular claim uri lbry://something
|
// accepts a regular claim uri lbry://something
|
||||||
// returns the channel uri that created this claim lbry://@channel
|
// returns the channel uri that created this claim lbry://@channel
|
||||||
|
@ -803,54 +751,3 @@ export const selectMyStreamUrlsCount = createSelector(
|
||||||
selectMyClaimUrisWithoutChannels,
|
selectMyClaimUrisWithoutChannels,
|
||||||
channels => channels.length
|
channels => channels.length
|
||||||
);
|
);
|
||||||
|
|
||||||
export const makeSelectResolvedRecommendedContentForUri = (
|
|
||||||
uri: string,
|
|
||||||
size: number,
|
|
||||||
claimId: string,
|
|
||||||
claimName: string,
|
|
||||||
claimTitle: string
|
|
||||||
) =>
|
|
||||||
createSelector(
|
|
||||||
makeSelectClaimForUri(uri),
|
|
||||||
selectResolvedSearchResultsByQuery,
|
|
||||||
makeSelectClaimIsNsfw(uri),
|
|
||||||
(claim, resolvedResultsByQuery, isMature) => {
|
|
||||||
const atVanityURI = !uri.includes('#');
|
|
||||||
|
|
||||||
let currentUri;
|
|
||||||
let recommendedContent;
|
|
||||||
let title;
|
|
||||||
if (claim) {
|
|
||||||
// always grab full URL - this can change once search returns canonical
|
|
||||||
currentUri = buildURI({ streamClaimId: claim.claim_id, streamName: claim.name });
|
|
||||||
title = claim.value ? claim.value.title : null;
|
|
||||||
} else {
|
|
||||||
// for cases on mobile where the claim may not have been resolved ()
|
|
||||||
currentUri = buildURI({ streamClaimId: claimId, streamName: claimName });
|
|
||||||
title = claimTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!title) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const options: {
|
|
||||||
related_to?: string,
|
|
||||||
nsfw?: boolean,
|
|
||||||
isBackgroundSearch?: boolean,
|
|
||||||
} = { related_to: claim ? claim.claim_id : claimId, size, isBackgroundSearch: false };
|
|
||||||
|
|
||||||
const searchQuery = getSearchQueryString(title.replace(/\//, ' '), options);
|
|
||||||
let results = resolvedResultsByQuery[searchQuery];
|
|
||||||
if (results) {
|
|
||||||
results = results.filter(
|
|
||||||
result =>
|
|
||||||
buildURI({ streamClaimId: result.claimId, streamName: result.name }) !== currentUri
|
|
||||||
);
|
|
||||||
recommendedContent = results;
|
|
||||||
}
|
|
||||||
|
|
||||||
return recommendedContent;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
|
@ -1,187 +0,0 @@
|
||||||
// @flow
|
|
||||||
import { SEARCH_TYPES, SEARCH_OPTIONS } from 'constants/search';
|
|
||||||
import { getSearchQueryString } from 'util/query-params';
|
|
||||||
import { normalizeURI, parseURI } from 'lbryURI';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
|
|
||||||
type State = { search: SearchState };
|
|
||||||
|
|
||||||
export const selectState = (state: State): SearchState => state.search;
|
|
||||||
|
|
||||||
export const selectSearchValue: (state: State) => string = createSelector(
|
|
||||||
selectState,
|
|
||||||
state => state.searchQuery
|
|
||||||
);
|
|
||||||
|
|
||||||
export const selectSearchOptions: (state: State) => SearchOptions = createSelector(
|
|
||||||
selectState,
|
|
||||||
state => state.options
|
|
||||||
);
|
|
||||||
|
|
||||||
export const selectSuggestions: (
|
|
||||||
state: State
|
|
||||||
) => { [string]: Array<SearchSuggestion> } = createSelector(
|
|
||||||
selectState,
|
|
||||||
state => state.suggestions
|
|
||||||
);
|
|
||||||
|
|
||||||
export const selectIsSearching: (state: State) => boolean = createSelector(
|
|
||||||
selectState,
|
|
||||||
state => state.searching
|
|
||||||
);
|
|
||||||
|
|
||||||
export const selectSearchUrisByQuery: (
|
|
||||||
state: State
|
|
||||||
) => { [string]: Array<string> } = createSelector(
|
|
||||||
selectState,
|
|
||||||
state => state.urisByQuery
|
|
||||||
);
|
|
||||||
|
|
||||||
export const makeSelectSearchUris = (query: string): ((state: State) => Array<string>) =>
|
|
||||||
// replace statement below is kind of ugly, and repeated in doSearch action
|
|
||||||
createSelector(
|
|
||||||
selectSearchUrisByQuery,
|
|
||||||
byQuery => byQuery[query ? query.replace(/^lbry:\/\//i, '').replace(/\//, ' ') : query]
|
|
||||||
);
|
|
||||||
|
|
||||||
export const selectResolvedSearchResultsByQuery: (
|
|
||||||
state: State
|
|
||||||
) => { [string]: Array<ResolvedSearchResult> } = createSelector(
|
|
||||||
selectState,
|
|
||||||
state => state.resolvedResultsByQuery
|
|
||||||
);
|
|
||||||
|
|
||||||
export const selectResolvedSearchResultsByQueryLastPageReached: (
|
|
||||||
state: State
|
|
||||||
) => { [string]: Array<boolean> } = createSelector(
|
|
||||||
selectState,
|
|
||||||
state => state.resolvedResultsByQueryLastPageReached
|
|
||||||
);
|
|
||||||
|
|
||||||
export const makeSelectResolvedSearchResults = (
|
|
||||||
query: string
|
|
||||||
): ((state: State) => Array<ResolvedSearchResult>) =>
|
|
||||||
// replace statement below is kind of ugly, and repeated in doSearch action
|
|
||||||
createSelector(
|
|
||||||
selectResolvedSearchResultsByQuery,
|
|
||||||
byQuery => byQuery[query ? query.replace(/^lbry:\/\//i, '').replace(/\//, ' ') : query]
|
|
||||||
);
|
|
||||||
|
|
||||||
export const makeSelectResolvedSearchResultsLastPageReached = (
|
|
||||||
query: string
|
|
||||||
): ((state: State) => boolean) =>
|
|
||||||
// replace statement below is kind of ugly, and repeated in doSearch action
|
|
||||||
createSelector(
|
|
||||||
selectResolvedSearchResultsByQueryLastPageReached,
|
|
||||||
byQuery => byQuery[query ? query.replace(/^lbry:\/\//i, '').replace(/\//, ' ') : query]
|
|
||||||
);
|
|
||||||
|
|
||||||
export const selectSearchBarFocused: boolean = createSelector(
|
|
||||||
selectState,
|
|
||||||
state => state.focused
|
|
||||||
);
|
|
||||||
|
|
||||||
export const selectSearchSuggestions: Array<SearchSuggestion> = createSelector(
|
|
||||||
selectSearchValue,
|
|
||||||
selectSuggestions,
|
|
||||||
(query: string, suggestions: { [string]: Array<string> }) => {
|
|
||||||
if (!query) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const queryIsPrefix =
|
|
||||||
query === 'lbry:' || query === 'lbry:/' || query === 'lbry://' || query === 'lbry://@';
|
|
||||||
|
|
||||||
if (queryIsPrefix) {
|
|
||||||
// If it is a prefix, wait until something else comes to figure out what to do
|
|
||||||
return [];
|
|
||||||
} else if (query.startsWith('lbry://')) {
|
|
||||||
// If it starts with a prefix, don't show any autocomplete results
|
|
||||||
// They are probably typing/pasting in a lbry uri
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
value: query,
|
|
||||||
type: query[7] === '@' ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
let searchSuggestions = [];
|
|
||||||
try {
|
|
||||||
const uri = normalizeURI(query);
|
|
||||||
const { channelName, streamName, isChannel } = parseURI(uri);
|
|
||||||
searchSuggestions.push(
|
|
||||||
{
|
|
||||||
value: query,
|
|
||||||
type: SEARCH_TYPES.SEARCH,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: uri,
|
|
||||||
shorthand: isChannel ? channelName : streamName,
|
|
||||||
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
searchSuggestions.push({
|
|
||||||
value: query,
|
|
||||||
type: SEARCH_TYPES.SEARCH,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
searchSuggestions.push({
|
|
||||||
value: query,
|
|
||||||
type: SEARCH_TYPES.TAG,
|
|
||||||
});
|
|
||||||
|
|
||||||
const apiSuggestions = suggestions[query] || [];
|
|
||||||
if (apiSuggestions.length) {
|
|
||||||
searchSuggestions = searchSuggestions.concat(
|
|
||||||
apiSuggestions
|
|
||||||
.filter(suggestion => suggestion !== query)
|
|
||||||
.map(suggestion => {
|
|
||||||
// determine if it's a channel
|
|
||||||
try {
|
|
||||||
const uri = normalizeURI(suggestion);
|
|
||||||
const { channelName, streamName, isChannel } = parseURI(uri);
|
|
||||||
|
|
||||||
return {
|
|
||||||
value: uri,
|
|
||||||
shorthand: isChannel ? channelName : streamName,
|
|
||||||
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE,
|
|
||||||
};
|
|
||||||
} catch (e) {
|
|
||||||
// search result includes some character that isn't valid in claim names
|
|
||||||
return {
|
|
||||||
value: suggestion,
|
|
||||||
type: SEARCH_TYPES.SEARCH,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return searchSuggestions;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Creates a query string based on the state in the search reducer
|
|
||||||
// Can be overrided by passing in custom sizes/from values for other areas pagination
|
|
||||||
|
|
||||||
type CustomOptions = {
|
|
||||||
isBackgroundSearch?: boolean,
|
|
||||||
size?: number,
|
|
||||||
from?: number,
|
|
||||||
related_to?: string,
|
|
||||||
nsfw?: boolean,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const makeSelectQueryWithOptions = (customQuery: ?string, options: CustomOptions) =>
|
|
||||||
createSelector(
|
|
||||||
selectSearchValue,
|
|
||||||
selectSearchOptions,
|
|
||||||
(query, defaultOptions) => {
|
|
||||||
const searchOptions = { ...defaultOptions, ...options };
|
|
||||||
const queryString = getSearchQueryString(customQuery || query, searchOptions);
|
|
||||||
|
|
||||||
return queryString;
|
|
||||||
}
|
|
||||||
);
|
|
|
@ -1,8 +1,4 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { SEARCH_OPTIONS } from 'constants/search';
|
|
||||||
|
|
||||||
const DEFAULT_SEARCH_RESULT_FROM = 0;
|
|
||||||
const DEFAULT_SEARCH_SIZE = 20;
|
|
||||||
|
|
||||||
export function parseQueryParams(queryString: string) {
|
export function parseQueryParams(queryString: string) {
|
||||||
if (queryString === '') return {};
|
if (queryString === '') return {};
|
||||||
|
@ -32,54 +28,3 @@ export function toQueryString(params: { [string]: string | number }) {
|
||||||
|
|
||||||
return parts.join('&');
|
return parts.join('&');
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getSearchQueryString = (query: string, options: any = {}) => {
|
|
||||||
const encodedQuery = encodeURIComponent(query);
|
|
||||||
const queryParams = [
|
|
||||||
`s=${encodedQuery}`,
|
|
||||||
`size=${options.size || DEFAULT_SEARCH_SIZE}`,
|
|
||||||
`from=${options.from || DEFAULT_SEARCH_RESULT_FROM}`,
|
|
||||||
];
|
|
||||||
const { isBackgroundSearch } = options;
|
|
||||||
const includeUserOptions =
|
|
||||||
typeof isBackgroundSearch === 'undefined' ? false : !isBackgroundSearch;
|
|
||||||
|
|
||||||
if (includeUserOptions) {
|
|
||||||
const claimType = options[SEARCH_OPTIONS.CLAIM_TYPE];
|
|
||||||
if (claimType) {
|
|
||||||
queryParams.push(`claimType=${claimType}`);
|
|
||||||
|
|
||||||
// If they are only searching for channels, strip out the media info
|
|
||||||
if (!claimType.includes(SEARCH_OPTIONS.INCLUDE_CHANNELS)) {
|
|
||||||
queryParams.push(
|
|
||||||
`mediaType=${[
|
|
||||||
SEARCH_OPTIONS.MEDIA_FILE,
|
|
||||||
SEARCH_OPTIONS.MEDIA_AUDIO,
|
|
||||||
SEARCH_OPTIONS.MEDIA_VIDEO,
|
|
||||||
SEARCH_OPTIONS.MEDIA_TEXT,
|
|
||||||
SEARCH_OPTIONS.MEDIA_IMAGE,
|
|
||||||
SEARCH_OPTIONS.MEDIA_APPLICATION,
|
|
||||||
].reduce(
|
|
||||||
(acc, currentOption) => (options[currentOption] ? `${acc}${currentOption},` : acc),
|
|
||||||
''
|
|
||||||
)}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const additionalOptions = {};
|
|
||||||
const { related_to } = options;
|
|
||||||
const { nsfw } = options;
|
|
||||||
if (related_to) additionalOptions['related_to'] = related_to;
|
|
||||||
if (typeof nsfw !== 'undefined') additionalOptions['nsfw'] = nsfw;
|
|
||||||
|
|
||||||
if (additionalOptions) {
|
|
||||||
Object.keys(additionalOptions).forEach(key => {
|
|
||||||
const option = additionalOptions[key];
|
|
||||||
queryParams.push(`${key}=${option}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return queryParams.join('&');
|
|
||||||
};
|
|
||||||
|
|
Loading…
Reference in a new issue