add doResolvedSearch actions which returns resolved search results (#258)
* add doResolvedSearch actions which returns resolved search results * add recommended content selector * update ResolvedSearchResult type * support for multiple pages of resolved search results
This commit is contained in:
parent
f0891dd298
commit
17a5260c3f
10 changed files with 388 additions and 15 deletions
154
dist/bundle.es.js
vendored
154
dist/bundle.es.js
vendored
|
@ -15,7 +15,6 @@ const CHANNEL_NEW = 'new';
|
|||
const PAGE_SIZE = 20;
|
||||
|
||||
var claim = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
MINIMUM_PUBLISH_BID: MINIMUM_PUBLISH_BID,
|
||||
CHANNEL_ANONYMOUS: CHANNEL_ANONYMOUS,
|
||||
CHANNEL_NEW: CHANNEL_NEW,
|
||||
|
@ -163,6 +162,9 @@ const DELETE_PURCHASED_URI = 'DELETE_PURCHASED_URI';
|
|||
const SEARCH_START = 'SEARCH_START';
|
||||
const SEARCH_SUCCESS = 'SEARCH_SUCCESS';
|
||||
const SEARCH_FAIL = 'SEARCH_FAIL';
|
||||
const RESOLVED_SEARCH_START = 'RESOLVED_SEARCH_START';
|
||||
const RESOLVED_SEARCH_SUCCESS = 'RESOLVED_SEARCH_SUCCESS';
|
||||
const RESOLVED_SEARCH_FAIL = 'RESOLVED_SEARCH_FAIL';
|
||||
const UPDATE_SEARCH_QUERY = 'UPDATE_SEARCH_QUERY';
|
||||
const UPDATE_SEARCH_OPTIONS = 'UPDATE_SEARCH_OPTIONS';
|
||||
const UPDATE_SEARCH_SUGGESTIONS = 'UPDATE_SEARCH_SUGGESTIONS';
|
||||
|
@ -275,7 +277,6 @@ const TOGGLE_BLOCK_CHANNEL = 'TOGGLE_BLOCK_CHANNEL';
|
|||
const USER_STATE_POPULATE = 'USER_STATE_POPULATE';
|
||||
|
||||
var action_types = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
WINDOW_FOCUSED: WINDOW_FOCUSED,
|
||||
DAEMON_READY: DAEMON_READY,
|
||||
DAEMON_VERSION_MATCH: DAEMON_VERSION_MATCH,
|
||||
|
@ -403,6 +404,9 @@ var action_types = /*#__PURE__*/Object.freeze({
|
|||
SEARCH_START: SEARCH_START,
|
||||
SEARCH_SUCCESS: SEARCH_SUCCESS,
|
||||
SEARCH_FAIL: SEARCH_FAIL,
|
||||
RESOLVED_SEARCH_START: RESOLVED_SEARCH_START,
|
||||
RESOLVED_SEARCH_SUCCESS: RESOLVED_SEARCH_SUCCESS,
|
||||
RESOLVED_SEARCH_FAIL: RESOLVED_SEARCH_FAIL,
|
||||
UPDATE_SEARCH_QUERY: UPDATE_SEARCH_QUERY,
|
||||
UPDATE_SEARCH_OPTIONS: UPDATE_SEARCH_OPTIONS,
|
||||
UPDATE_SEARCH_SUGGESTIONS: UPDATE_SEARCH_SUGGESTIONS,
|
||||
|
@ -518,7 +522,6 @@ const OTHER = 'other';
|
|||
const COPYRIGHT = 'copyright';
|
||||
|
||||
var licenses = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
CC_LICENSES: CC_LICENSES,
|
||||
NONE: NONE,
|
||||
PUBLIC_DOMAIN: PUBLIC_DOMAIN,
|
||||
|
@ -549,7 +552,6 @@ const HISTORY = 'user_history';
|
|||
const WALLET = 'wallet';
|
||||
|
||||
var pages = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
AUTH: AUTH,
|
||||
BACKUP: BACKUP,
|
||||
CHANNEL: CHANNEL,
|
||||
|
@ -599,7 +601,6 @@ const RECEIVE_INTERESTS_NOTIFICATIONS = 'receiveInterestsNotifications';
|
|||
const RECEIVE_CREATOR_NOTIFICATIONS = 'receiveCreatorNotifications';
|
||||
|
||||
var settings = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
CREDIT_REQUIRED_ACKNOWLEDGED: CREDIT_REQUIRED_ACKNOWLEDGED,
|
||||
NEW_USER_ACKNOWLEDGED: NEW_USER_ACKNOWLEDGED,
|
||||
EMAIL_COLLECTION_ACKNOWLEDGED: EMAIL_COLLECTION_ACKNOWLEDGED,
|
||||
|
@ -627,7 +628,6 @@ const TITLE = 'title';
|
|||
const FILENAME = 'filename';
|
||||
|
||||
var sort_options = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
DATE_NEW: DATE_NEW,
|
||||
DATE_OLD: DATE_OLD,
|
||||
TITLE: TITLE,
|
||||
|
@ -641,7 +641,6 @@ const COMPLETE = 'complete';
|
|||
const MANUAL = 'manual';
|
||||
|
||||
var thumbnail_upload_statuses = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
API_DOWN: API_DOWN,
|
||||
READY: READY,
|
||||
IN_PROGRESS: IN_PROGRESS,
|
||||
|
@ -661,7 +660,6 @@ const UPDATE = 'update';
|
|||
const ABANDON = 'abandon';
|
||||
|
||||
var transaction_types = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
ALL: ALL,
|
||||
SPEND: SPEND,
|
||||
RECEIVE: RECEIVE,
|
||||
|
@ -678,7 +676,6 @@ const PAGE_SIZE$1 = 50;
|
|||
const LATEST_PAGE_SIZE = 20;
|
||||
|
||||
var transaction_list = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
PAGE_SIZE: PAGE_SIZE$1,
|
||||
LATEST_PAGE_SIZE: LATEST_PAGE_SIZE
|
||||
});
|
||||
|
@ -687,7 +684,6 @@ const SPEECH_STATUS = 'https://spee.ch/api/config/site/publishing';
|
|||
const SPEECH_PUBLISH = 'https://spee.ch/api/claim/publish';
|
||||
|
||||
var speech_urls = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
SPEECH_STATUS: SPEECH_STATUS,
|
||||
SPEECH_PUBLISH: SPEECH_PUBLISH
|
||||
});
|
||||
|
@ -733,7 +729,6 @@ const WALLET_DIR = 'wallet_dir';
|
|||
const WALLETS = 'wallets';
|
||||
|
||||
var daemon_settings = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
ANNOUNCE_HEAD_AND_SD_ONLY: ANNOUNCE_HEAD_AND_SD_ONLY,
|
||||
API: API,
|
||||
BLOB_DOWNLOAD_TIMEOUT: BLOB_DOWNLOAD_TIMEOUT,
|
||||
|
@ -787,7 +782,6 @@ var daemon_settings = /*#__PURE__*/Object.freeze({
|
|||
const WALLET_SERVERS = LBRYUM_SERVERS;
|
||||
|
||||
var shared_preferences = /*#__PURE__*/Object.freeze({
|
||||
__proto__: null,
|
||||
WALLET_SERVERS: WALLET_SERVERS
|
||||
});
|
||||
|
||||
|
@ -1342,6 +1336,18 @@ const makeSelectSearchUris = query =>
|
|||
// replace statement below is kind of ugly, and repeated in doSearch action
|
||||
reselect.createSelector(selectSearchUrisByQuery, byQuery => byQuery[query ? query.replace(/^lbry:\/\//i, '').replace(/\//, ' ') : query]);
|
||||
|
||||
const selectResolvedSearchResultsByQuery = reselect.createSelector(selectState, state => state.resolvedResultsByQuery);
|
||||
|
||||
const selectResolvedSearchResultsByQueryLastPageReached = reselect.createSelector(selectState, state => state.resolvedResultsByQueryLastPageReached);
|
||||
|
||||
const makeSelectResolvedSearchResults = query =>
|
||||
// replace statement below is kind of ugly, and repeated in doSearch action
|
||||
reselect.createSelector(selectResolvedSearchResultsByQuery, byQuery => byQuery[query ? query.replace(/^lbry:\/\//i, '').replace(/\//, ' ') : query]);
|
||||
|
||||
const makeSelectResolvedSearchResultsLastPageReached = query =>
|
||||
// replace statement below is kind of ugly, and repeated in doSearch action
|
||||
reselect.createSelector(selectResolvedSearchResultsByQueryLastPageReached, byQuery => byQuery[query ? query.replace(/^lbry:\/\//i, '').replace(/\//, ' ') : query]);
|
||||
|
||||
const selectSearchBarFocused = reselect.createSelector(selectState, state => state.focused);
|
||||
|
||||
const selectSearchSuggestions = reselect.createSelector(selectSearchValue, selectSuggestions, (query, suggestions) => {
|
||||
|
@ -2277,6 +2283,34 @@ const makeSelectMyStreamUrlsForPage = (page = 1) => reselect.createSelector(sele
|
|||
|
||||
const selectMyStreamUrlsCount = reselect.createSelector(selectMyClaimUrisWithoutChannels, channels => channels.length);
|
||||
|
||||
const makeSelectResolvedRecommendedContentForUri = (uri, size) => reselect.createSelector(makeSelectClaimForUri(uri), selectResolvedSearchResultsByQuery, (claim, resolvedResultsByQuery) => {
|
||||
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 searchQuery = getSearchQueryString(title.replace(/\//, ' '), { size }, undefined, {
|
||||
related_to: claim.claim_id
|
||||
});
|
||||
|
||||
let results = resolvedResultsByQuery[searchQuery];
|
||||
if (results) {
|
||||
results = results.filter(result => buildURI({ streamClaimId: result.claimId, streamName: result.name }) !== currentUri);
|
||||
recommendedContent = results;
|
||||
}
|
||||
}
|
||||
|
||||
return recommendedContent;
|
||||
});
|
||||
|
||||
function numberWithCommas(x) {
|
||||
var parts = x.toString().split('.');
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
|
@ -4020,6 +4054,63 @@ from, isBackgroundSearch = false, options = {}, resolveResults = true) => (dispa
|
|||
});
|
||||
};
|
||||
|
||||
const doResolvedSearch = (rawQuery, size, // only pass in if you don't want to use the users setting (ex: related content)
|
||||
from, isBackgroundSearch = false, options = {}) => (dispatch, getState) => {
|
||||
const query = rawQuery.replace(/^lbry:\/\//i, '').replace(/\//, ' ');
|
||||
|
||||
if (!query) {
|
||||
dispatch({
|
||||
type: RESOLVED_SEARCH_FAIL
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const state = getState();
|
||||
let queryWithOptions = makeSelectQueryWithOptions(query, size, from, isBackgroundSearch, options)(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, size, null, isBackgroundSearch, options)(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: RESOLVED_SEARCH_START
|
||||
});
|
||||
|
||||
if (!state.search.searchQuery && !isBackgroundSearch) {
|
||||
dispatch(doUpdateSearchQuery(query));
|
||||
}
|
||||
|
||||
fetch(`${CONNECTION_STRING}search?resolve=true&${queryWithOptions}`).then(handleFetchResponse).then(data => {
|
||||
const results = [];
|
||||
|
||||
data.forEach(result => {
|
||||
if (result) {
|
||||
results.push(result);
|
||||
}
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: RESOLVED_SEARCH_SUCCESS,
|
||||
data: {
|
||||
query: queryWithoutFrom,
|
||||
results,
|
||||
pageSize: size,
|
||||
append: parseInt(from, 10) > parseInt(size, 10) - 1
|
||||
}
|
||||
});
|
||||
}).catch(e => {
|
||||
dispatch({
|
||||
type: RESOLVED_SEARCH_FAIL
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const doFocusSearchInput = () => dispatch => dispatch({
|
||||
type: SEARCH_FOCUS
|
||||
});
|
||||
|
@ -5024,7 +5115,9 @@ const defaultState$7 = {
|
|||
[SEARCH_OPTIONS.MEDIA_APPLICATION]: true
|
||||
},
|
||||
suggestions: {},
|
||||
urisByQuery: {}
|
||||
urisByQuery: {},
|
||||
resolvedResultsByQuery: {},
|
||||
resolvedResultsByQueryLastPageReached: {}
|
||||
};
|
||||
|
||||
const searchReducer = handleActions({
|
||||
|
@ -5044,6 +5137,35 @@ const searchReducer = handleActions({
|
|||
searching: false
|
||||
}),
|
||||
|
||||
[RESOLVED_SEARCH_START]: state => _extends$d({}, state, {
|
||||
searching: true
|
||||
}),
|
||||
[RESOLVED_SEARCH_SUCCESS]: (state, action) => {
|
||||
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 _extends$d({}, state, {
|
||||
searching: false,
|
||||
resolvedResultsByQuery,
|
||||
resolvedResultsByQueryLastPageReached
|
||||
});
|
||||
},
|
||||
|
||||
[RESOLVED_SEARCH_FAIL]: state => _extends$d({}, state, {
|
||||
searching: false
|
||||
}),
|
||||
|
||||
[UPDATE_SEARCH_QUERY]: (state, action) => _extends$d({}, state, {
|
||||
searchQuery: action.data.query,
|
||||
isActive: true
|
||||
|
@ -5612,6 +5734,7 @@ exports.doPurchaseUri = doPurchaseUri;
|
|||
exports.doResetThumbnailStatus = doResetThumbnailStatus;
|
||||
exports.doResolveUri = doResolveUri;
|
||||
exports.doResolveUris = doResolveUris;
|
||||
exports.doResolvedSearch = doResolvedSearch;
|
||||
exports.doSearch = doSearch;
|
||||
exports.doSendDraftTransaction = doSendDraftTransaction;
|
||||
exports.doSendTip = doSendTip;
|
||||
|
@ -5680,6 +5803,9 @@ exports.makeSelectPermanentUrlForUri = makeSelectPermanentUrlForUri;
|
|||
exports.makeSelectPublishFormValue = makeSelectPublishFormValue;
|
||||
exports.makeSelectQueryWithOptions = makeSelectQueryWithOptions;
|
||||
exports.makeSelectRecommendedContentForUri = makeSelectRecommendedContentForUri;
|
||||
exports.makeSelectResolvedRecommendedContentForUri = makeSelectResolvedRecommendedContentForUri;
|
||||
exports.makeSelectResolvedSearchResults = makeSelectResolvedSearchResults;
|
||||
exports.makeSelectResolvedSearchResultsLastPageReached = makeSelectResolvedSearchResultsLastPageReached;
|
||||
exports.makeSelectSearchDownloadUrlsCount = makeSelectSearchDownloadUrlsCount;
|
||||
exports.makeSelectSearchDownloadUrlsForPage = makeSelectSearchDownloadUrlsForPage;
|
||||
exports.makeSelectSearchUris = makeSelectSearchUris;
|
||||
|
@ -5772,6 +5898,8 @@ exports.selectPurchasedUris = selectPurchasedUris;
|
|||
exports.selectReceiveAddress = selectReceiveAddress;
|
||||
exports.selectRecentTransactions = selectRecentTransactions;
|
||||
exports.selectReservedBalance = selectReservedBalance;
|
||||
exports.selectResolvedSearchResultsByQuery = selectResolvedSearchResultsByQuery;
|
||||
exports.selectResolvedSearchResultsByQueryLastPageReached = selectResolvedSearchResultsByQueryLastPageReached;
|
||||
exports.selectResolvingUris = selectResolvingUris;
|
||||
exports.selectSearchBarFocused = selectSearchBarFocused;
|
||||
exports.selectSearchOptions = selectSearchOptions;
|
||||
|
|
25
dist/flow-typed/Search.js
vendored
25
dist/flow-typed/Search.js
vendored
|
@ -28,6 +28,8 @@ declare type SearchState = {
|
|||
options: SearchOptions,
|
||||
suggestions: { [string]: Array<SearchSuggestion> },
|
||||
urisByQuery: {},
|
||||
resolvedResultsByQuery: {},
|
||||
resolvedResultsByQueryLastPageReached: {},
|
||||
};
|
||||
|
||||
declare type SearchSuccess = {
|
||||
|
@ -57,3 +59,26 @@ 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,
|
||||
},
|
||||
};
|
||||
|
|
25
flow-typed/Search.js
vendored
25
flow-typed/Search.js
vendored
|
@ -28,6 +28,8 @@ declare type SearchState = {
|
|||
options: SearchOptions,
|
||||
suggestions: { [string]: Array<SearchSuggestion> },
|
||||
urisByQuery: {},
|
||||
resolvedResultsByQuery: {},
|
||||
resolvedResultsByQueryLastPageReached: {},
|
||||
};
|
||||
|
||||
declare type SearchSuccess = {
|
||||
|
@ -57,3 +59,26 @@ 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,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -139,6 +139,9 @@ export const DELETE_PURCHASED_URI = 'DELETE_PURCHASED_URI';
|
|||
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';
|
||||
|
|
|
@ -510,5 +510,5 @@ export const DEFAULT_KNOWN_TAGS = [
|
|||
'portugal',
|
||||
'dantdm',
|
||||
'teaser',
|
||||
'lbry'
|
||||
'lbry',
|
||||
];
|
||||
|
|
|
@ -90,6 +90,7 @@ export {
|
|||
|
||||
export {
|
||||
doSearch,
|
||||
doResolvedSearch,
|
||||
doUpdateSearchQuery,
|
||||
doFocusSearchInput,
|
||||
doBlurSearchInput,
|
||||
|
@ -183,6 +184,7 @@ export {
|
|||
makeSelectOmittedCountForChannel,
|
||||
makeSelectClaimIsNsfw,
|
||||
makeSelectRecommendedContentForUri,
|
||||
makeSelectResolvedRecommendedContentForUri,
|
||||
makeSelectFirstRecommendedFileForUri,
|
||||
makeSelectChannelForClaimUri,
|
||||
makeSelectClaimIsPending,
|
||||
|
@ -265,9 +267,13 @@ export {
|
|||
export { selectSearchState };
|
||||
export {
|
||||
makeSelectSearchUris,
|
||||
makeSelectResolvedSearchResults,
|
||||
makeSelectResolvedSearchResultsLastPageReached,
|
||||
selectSearchValue,
|
||||
selectSearchOptions,
|
||||
selectIsSearching,
|
||||
selectResolvedSearchResultsByQuery,
|
||||
selectResolvedSearchResultsByQueryLastPageReached,
|
||||
selectSearchUrisByQuery,
|
||||
selectSearchBarFocused,
|
||||
selectSearchSuggestions,
|
||||
|
|
|
@ -4,6 +4,7 @@ import { buildURI } from 'lbryURI';
|
|||
import { doResolveUri } from 'redux/actions/claims';
|
||||
import {
|
||||
makeSelectSearchUris,
|
||||
makeSelectResolvedSearchResults,
|
||||
selectSuggestions,
|
||||
makeSelectQueryWithOptions,
|
||||
selectSearchValue,
|
||||
|
@ -159,6 +160,77 @@ export const doSearch = (
|
|||
});
|
||||
};
|
||||
|
||||
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,
|
||||
} = {}
|
||||
) => (dispatch: Dispatch, getState: GetState) => {
|
||||
const query = rawQuery.replace(/^lbry:\/\//i, '').replace(/\//, ' ');
|
||||
|
||||
if (!query) {
|
||||
dispatch({
|
||||
type: ACTIONS.RESOLVED_SEARCH_FAIL,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const state = getState();
|
||||
let queryWithOptions = makeSelectQueryWithOptions(query, size, from, isBackgroundSearch, options)(
|
||||
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, size, null, isBackgroundSearch, options)(
|
||||
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));
|
||||
}
|
||||
|
||||
fetch(`${CONNECTION_STRING}search?resolve=true&${queryWithOptions}`)
|
||||
.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,
|
||||
|
|
|
@ -18,6 +18,8 @@ const defaultState = {
|
|||
},
|
||||
suggestions: {},
|
||||
urisByQuery: {},
|
||||
resolvedResultsByQuery: {},
|
||||
resolvedResultsByQueryLastPageReached: {},
|
||||
};
|
||||
|
||||
export const searchReducer = handleActions(
|
||||
|
@ -41,6 +43,47 @@ export const searchReducer = handleActions(
|
|||
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
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
// @flow
|
||||
import { normalizeURI, buildURI, parseURI } from 'lbryURI';
|
||||
import { selectSearchUrisByQuery } from 'redux/selectors/search';
|
||||
import {
|
||||
selectResolvedSearchResultsByQuery,
|
||||
selectSearchUrisByQuery,
|
||||
} from 'redux/selectors/search';
|
||||
import { selectSupportsByOutpoint } from 'redux/selectors/wallet';
|
||||
import { createSelector } from 'reselect';
|
||||
import { isClaimNsfw, createNormalizedClaimSearchKey } from 'util/claim';
|
||||
|
@ -639,3 +642,39 @@ export const selectMyStreamUrlsCount = createSelector(
|
|||
selectMyClaimUrisWithoutChannels,
|
||||
channels => channels.length
|
||||
);
|
||||
|
||||
export const makeSelectResolvedRecommendedContentForUri = (uri: string, size: number) =>
|
||||
createSelector(
|
||||
makeSelectClaimForUri(uri),
|
||||
selectResolvedSearchResultsByQuery,
|
||||
(claim, resolvedResultsByQuery) => {
|
||||
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 searchQuery = getSearchQueryString(title.replace(/\//, ' '), { size }, undefined, {
|
||||
related_to: claim.claim_id,
|
||||
});
|
||||
|
||||
let results = resolvedResultsByQuery[searchQuery];
|
||||
if (results) {
|
||||
results = results.filter(
|
||||
result =>
|
||||
buildURI({ streamClaimId: result.claimId, streamName: result.name }) !== currentUri
|
||||
);
|
||||
recommendedContent = results;
|
||||
}
|
||||
}
|
||||
|
||||
return recommendedContent;
|
||||
}
|
||||
);
|
||||
|
|
|
@ -44,6 +44,38 @@ export const makeSelectSearchUris = (query: string): ((state: State) => Array<st
|
|||
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
|
||||
|
|
Loading…
Reference in a new issue