add search suggestions selector

This commit is contained in:
Sean Yesmunt 2018-08-14 23:57:35 -04:00
parent 8794a775bf
commit dc6f19bb7d
5 changed files with 247 additions and 217 deletions

275
dist/bundle.js vendored
View file

@ -104,7 +104,7 @@ return /******/ (function(modules) { // webpackBootstrap
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.selectWalletUnlockResult = exports.selectWalletUnlockSucceeded = exports.selectWalletUnlockPending = exports.selectWalletDecryptResult = exports.selectWalletDecryptSucceeded = exports.selectWalletDecryptPending = exports.selectWalletEncryptResult = exports.selectWalletEncryptSucceeded = exports.selectWalletEncryptPending = exports.selectWalletState = exports.selectWalletIsEncrypted = exports.selectBlocks = exports.selectDraftTransactionError = exports.selectDraftTransactionAddress = exports.selectDraftTransactionAmount = exports.selectDraftTransaction = exports.selectGettingNewAddress = exports.selectReceiveAddress = exports.selectIsSendingSupport = exports.selectIsFetchingTransactions = exports.selectHasTransactions = exports.selectRecentTransactions = exports.selectTransactionItems = exports.selectTransactionsById = exports.selectBalance = exports.makeSelectBlockDate = exports.selectSearchBarFocused = exports.selectWunderBarAddress = exports.selectSearchUrisByQuery = exports.selectIsSearching = exports.selectSearchValue = exports.selectSearchQuery = exports.makeSelectSearchUris = exports.selectSearchState = exports.selectNavLinks = exports.selectActiveHistoryEntry = exports.selectHistoryStack = exports.selectHistoryIndex = exports.selectIsForwardDisabled = exports.selectIsBackDisabled = exports.selectPathAfterAuth = exports.selectPageTitle = exports.selectHeaderLinks = exports.selectCurrentParams = exports.selectCurrentPage = exports.selectCurrentPath = exports.makeSelectCurrentParam = exports.computePageFromPath = exports.selectSearchDownloadUris = exports.selectTotalDownloadProgress = exports.selectDownloadingFileInfos = exports.selectFileInfosDownloaded = exports.selectUrisLoading = exports.selectDownloadingByOutpoint = exports.selectIsFetchingFileListDownloadedOrPublished = exports.selectIsFetchingFileList = exports.selectFileInfosByOutpoint = exports.makeSelectLoadingForUri = exports.makeSelectDownloadingForUri = exports.makeSelectFileInfoForUri = exports.selectFetchingCostInfo = exports.selectCostForCurrentPageUri = exports.selectAllCostInfoByUri = exports.makeSelectCostInfoForUri = exports.makeSelectFetchingCostInfoForUri = exports.selectRewardContentClaimIds = exports.selectChannelClaimCounts = exports.selectPlayingUri = exports.selectFetchingTrendingUris = exports.selectTrendingUris = undefined;
exports.selectWalletUnlockResult = exports.selectWalletUnlockSucceeded = exports.selectWalletUnlockPending = exports.selectWalletDecryptResult = exports.selectWalletDecryptSucceeded = exports.selectWalletDecryptPending = exports.selectWalletEncryptResult = exports.selectWalletEncryptSucceeded = exports.selectWalletEncryptPending = exports.selectWalletState = exports.selectWalletIsEncrypted = exports.selectBlocks = exports.selectDraftTransactionError = exports.selectDraftTransactionAddress = exports.selectDraftTransactionAmount = exports.selectDraftTransaction = exports.selectGettingNewAddress = exports.selectReceiveAddress = exports.selectIsSendingSupport = exports.selectIsFetchingTransactions = exports.selectHasTransactions = exports.selectRecentTransactions = exports.selectTransactionItems = exports.selectTransactionsById = exports.selectBalance = exports.makeSelectBlockDate = exports.selectSearchSuggestions = exports.selectSearchBarFocused = exports.selectWunderBarAddress = exports.selectSearchUrisByQuery = exports.selectIsSearching = exports.selectSearchValue = exports.selectSearchQuery = exports.makeSelectSearchUris = exports.selectSearchState = exports.selectNavLinks = exports.selectActiveHistoryEntry = exports.selectHistoryStack = exports.selectHistoryIndex = exports.selectIsForwardDisabled = exports.selectIsBackDisabled = exports.selectPathAfterAuth = exports.selectPageTitle = exports.selectHeaderLinks = exports.selectCurrentParams = exports.selectCurrentPage = exports.selectCurrentPath = exports.makeSelectCurrentParam = exports.computePageFromPath = exports.selectSearchDownloadUris = exports.selectTotalDownloadProgress = exports.selectDownloadingFileInfos = exports.selectFileInfosDownloaded = exports.selectUrisLoading = exports.selectDownloadingByOutpoint = exports.selectIsFetchingFileListDownloadedOrPublished = exports.selectIsFetchingFileList = exports.selectFileInfosByOutpoint = exports.makeSelectLoadingForUri = exports.makeSelectDownloadingForUri = exports.makeSelectFileInfoForUri = exports.selectFetchingCostInfo = exports.selectCostForCurrentPageUri = exports.selectAllCostInfoByUri = exports.makeSelectCostInfoForUri = exports.makeSelectFetchingCostInfoForUri = exports.selectRewardContentClaimIds = exports.selectChannelClaimCounts = exports.selectPlayingUri = exports.selectFetchingTrendingUris = exports.selectTrendingUris = undefined;
exports.selectFetchingFeaturedUris = exports.selectFeaturedUris = exports.selectResolvingUris = exports.selectMyChannelClaims = exports.selectFetchingMyChannels = exports.selectMyClaimsOutpoints = exports.selectAllMyClaimsByOutpoint = exports.selectMyClaimsWithoutChannels = exports.selectMyClaims = exports.selectPendingClaims = exports.selectIsFetchingClaimListMine = exports.selectAllFetchingChannelClaims = exports.selectMyActiveClaims = exports.selectAbandoningIds = exports.selectMyClaimsRaw = exports.selectAllClaimsByChannel = exports.selectClaimsByUri = exports.selectClaimsById = exports.makeSelectRecommendedContentForUri = exports.makeSelectNsfwCountForChannel = exports.makeSelectNsfwCountFromUris = exports.makeSelectTotalPagesForChannel = exports.makeSelectTotalItemsForChannel = exports.makeSelectIsUriResolving = exports.makeSelectContentTypeForUri = exports.makeSelectTitleForUri = exports.makeSelectMetadataForUri = exports.makeSelectClaimsInChannelForPage = exports.makeSelectClaimsInChannelForCurrentPage = exports.makeSelectFetchingChannelClaims = exports.makeSelectClaimIsMine = exports.makeSelectClaimForUri = exports.selectSnack = exports.selectNotificationProps = exports.selectNotification = exports.selectBlackListedOutpoints = exports.blacklistReducer = exports.walletReducer = exports.searchReducer = exports.notificationsReducer = exports.fileInfoReducer = exports.costInfoReducer = exports.claimsReducer = exports.formatFullPrice = exports.formatCredits = exports.toQueryString = exports.parseQueryParams = exports.batchActions = exports.doWalletStatus = exports.doWalletUnlock = exports.doWalletDecrypt = exports.doWalletEncrypt = exports.doSendSupport = exports.doSetDraftTransactionAddress = exports.doSetDraftTransactionAmount = exports.doSendDraftTransaction = exports.doCheckAddressIsMine = exports.doGetNewAddress = exports.doFetchBlock = exports.doFetchTransactions = exports.doBalanceSubscribe = exports.doUpdateBalance = exports.doBlackListedOutpointsSubscribe = exports.doBlurSearchInput = exports.doFocusSearchInput = exports.doUpdateSearchQuery = exports.doSearch = exports.doFetchFileInfosAndPublishedClaims = exports.doFileList = exports.doFetchFileInfo = exports.doFetchCostInfoForUri = exports.doFetchRewardedContent = exports.doFetchTrendingUris = exports.doFetchFeaturedUris = exports.doResolveUri = exports.doResolveUris = exports.doAbandonClaim = exports.doFetchClaimListMine = exports.doFetchClaimCountByChannel = exports.doFetchClaimsByChannel = exports.doHideNotification = exports.doNotify = exports.convertToShareLink = exports.isNameValid = exports.isURIClaimable = exports.isURIValid = exports.normalizeURI = exports.buildURI = exports.parseURI = exports.regexAddress = exports.regexInvalidURI = exports.Lbryapi = exports.Lbry = exports.TRANSACTIONS = exports.SETTINGS = exports.SEARCH_TYPES = exports.THUMBNAIL_STATUSES = exports.MODALS = exports.ACTIONS = exports.Notification = undefined;
var _Notification = __webpack_require__(1);
@ -245,7 +245,7 @@ Object.defineProperty(exports, 'doFetchRewardedContent', {
}
});
var _cost_info = __webpack_require__(21);
var _cost_info = __webpack_require__(22);
Object.defineProperty(exports, 'doFetchCostInfoForUri', {
enumerable: true,
@ -254,7 +254,7 @@ Object.defineProperty(exports, 'doFetchCostInfoForUri', {
}
});
var _file_info = __webpack_require__(22);
var _file_info = __webpack_require__(23);
Object.defineProperty(exports, 'doFetchFileInfo', {
enumerable: true,
@ -275,7 +275,7 @@ Object.defineProperty(exports, 'doFetchFileInfosAndPublishedClaims', {
}
});
var _search = __webpack_require__(24);
var _search = __webpack_require__(25);
Object.defineProperty(exports, 'doSearch', {
enumerable: true,
@ -398,7 +398,7 @@ Object.defineProperty(exports, 'doWalletStatus', {
}
});
var _batchActions = __webpack_require__(20);
var _batchActions = __webpack_require__(21);
Object.defineProperty(exports, 'batchActions', {
enumerable: true,
@ -788,7 +788,7 @@ Object.defineProperty(exports, 'selectFetchingCostInfo', {
}
});
var _file_info3 = __webpack_require__(23);
var _file_info3 = __webpack_require__(24);
Object.defineProperty(exports, 'makeSelectFileInfoForUri', {
enumerable: true,
@ -994,6 +994,12 @@ Object.defineProperty(exports, 'selectSearchBarFocused', {
return _search3.selectSearchBarFocused;
}
});
Object.defineProperty(exports, 'selectSearchSuggestions', {
enumerable: true,
get: function get() {
return _search3.selectSearchSuggestions;
}
});
var _wallet3 = __webpack_require__(29);
@ -1166,7 +1172,7 @@ var _thumbnail_upload_statuses = __webpack_require__(44);
var THUMBNAIL_STATUSES = _interopRequireWildcard(_thumbnail_upload_statuses);
var _search4 = __webpack_require__(25);
var _search4 = __webpack_require__(19);
var SEARCH_TYPES = _interopRequireWildcard(_search4);
@ -1794,7 +1800,7 @@ var _notifications = __webpack_require__(3);
var _claims = __webpack_require__(14);
var _batchActions = __webpack_require__(20);
var _batchActions = __webpack_require__(21);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@ -3026,7 +3032,7 @@ var _search = __webpack_require__(18);
var _reselect = __webpack_require__(16);
var _claim = __webpack_require__(19);
var _claim = __webpack_require__(20);
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
@ -3704,12 +3710,20 @@ function toQueryString(params) {
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.selectSearchBarFocused = exports.selectWunderBarAddress = exports.makeSelectSearchUris = exports.selectSearchUrisByQuery = exports.selectIsSearching = exports.selectSearchQuery = exports.selectSearchValue = exports.selectState = undefined;
exports.selectSearchSuggestions = exports.selectSearchBarFocused = exports.selectWunderBarAddress = exports.makeSelectSearchUris = exports.selectSearchUrisByQuery = exports.selectIsSearching = exports.selectSearchQuery = exports.selectSuggestions = exports.selectSearchValue = exports.selectState = undefined;
var _search = __webpack_require__(19);
var SEARCH_TYPES = _interopRequireWildcard(_search);
var _lbryURI = __webpack_require__(2);
var _navigation = __webpack_require__(15);
var _reselect = __webpack_require__(16);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
var selectState = exports.selectState = function selectState(state) {
return state.search || {};
};
@ -3718,6 +3732,10 @@ var selectSearchValue = exports.selectSearchValue = (0, _reselect.createSelector
return state.searchQuery;
});
var selectSuggestions = exports.selectSuggestions = (0, _reselect.createSelector)(selectState, function (state) {
return state.suggestions;
});
var selectSearchQuery = exports.selectSearchQuery = (0, _reselect.createSelector)(_navigation.selectCurrentPage, _navigation.selectCurrentParams, function (page, params) {
return page === 'search' ? params && params.query : null;
});
@ -3751,6 +3769,80 @@ var selectWunderBarAddress = exports.selectWunderBarAddress = (0, _reselect.crea
var selectSearchBarFocused = exports.selectSearchBarFocused = (0, _reselect.createSelector)(selectState, function (state) {
return state.focused;
});
// export const selectSear
var selectSearchSuggestions = exports.selectSearchSuggestions = (0, _reselect.createSelector)(selectSearchValue, selectSuggestions, function (query, suggestions) {
if (!query) {
return [];
}
var queryIsPrefix = query === 'lbry:' || query === 'lbry:/' || query === 'lbry://';
if (query.startsWith('lbry://') && query !== '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: SEARCH_TYPES.FILE
}];
} else if (queryIsPrefix) {
// If it is a prefix, wait until something else comes to figure out what to do
return [];
}
var searchSuggestions = [];
try {
var uri = (0, _lbryURI.normalizeURI)(query);
var _parseURI = (0, _lbryURI.parseURI)(uri),
claimName = _parseURI.claimName,
isChannel = _parseURI.isChannel;
searchSuggestions.push({
value: claimName,
type: SEARCH_TYPES.SEARCH
}, {
value: uri,
shorthand: isChannel ? claimName.slice(1) : claimName,
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE
});
} catch (e) {
searchSuggestions.push({
value: query,
type: SEARCH_TYPES.SEARCH
});
}
var apiSuggestions = suggestions[query] || [];
if (apiSuggestions.length) {
searchSuggestions = searchSuggestions.concat(apiSuggestions.filter(function (suggestion) {
return suggestion !== query;
}).map(function (suggestion) {
// determine if it's a channel
try {
var _uri = (0, _lbryURI.normalizeURI)(suggestion);
var _parseURI2 = (0, _lbryURI.parseURI)(_uri),
_claimName = _parseURI2.claimName,
_isChannel = _parseURI2.isChannel;
return {
value: _uri,
shorthand: _isChannel ? _claimName.slice(1) : _claimName,
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;
});
/***/ }),
/* 19 */
@ -3759,6 +3851,20 @@ var selectSearchBarFocused = exports.selectSearchBarFocused = (0, _reselect.crea
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var FILE = exports.FILE = 'file';
var CHANNEL = exports.CHANNEL = 'channel';
var SEARCH = exports.SEARCH = 'search';
/***/ }),
/* 20 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
@ -3767,7 +3873,7 @@ var isClaimNsfw = exports.isClaimNsfw = function isClaimNsfw(claim) {
};
/***/ }),
/* 20 */
/* 21 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
@ -3790,7 +3896,7 @@ function batchActions() {
}
/***/ }),
/* 21 */
/* 22 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
@ -3850,7 +3956,7 @@ function doFetchCostInfoForUri(uri) {
}
/***/ }),
/* 22 */
/* 23 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
@ -3875,7 +3981,7 @@ var _claims = __webpack_require__(5);
var _claims2 = __webpack_require__(14);
var _file_info = __webpack_require__(23);
var _file_info = __webpack_require__(24);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@ -3943,7 +4049,7 @@ function doFetchFileInfosAndPublishedClaims() {
}
/***/ }),
/* 23 */
/* 24 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
@ -4150,7 +4256,7 @@ var selectSearchDownloadUris = exports.selectSearchDownloadUris = function selec
};
/***/ }),
/* 24 */
/* 25 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
@ -4165,17 +4271,13 @@ var _action_types = __webpack_require__(4);
var ACTIONS = _interopRequireWildcard(_action_types);
var _search = __webpack_require__(25);
var SEARCH_TYPES = _interopRequireWildcard(_search);
var _lbryURI = __webpack_require__(2);
var _claims = __webpack_require__(5);
var _search2 = __webpack_require__(18);
var _search = __webpack_require__(18);
var _batchActions = __webpack_require__(20);
var _batchActions = __webpack_require__(21);
var _handleFetch = __webpack_require__(26);
@ -4185,13 +4287,14 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
var DEFAULTSEARCHRESULTSIZE = 10; // @flow
// @flow
var DEFAULTSEARCHRESULTSIZE = 10;
var DEFAULTSEARCHRESULTFROM = 0;
var doSearch = exports.doSearch = function doSearch(rawQuery) {
var size = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULTSEARCHRESULTSIZE;
var from = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : DEFAULTSEARCHRESULTFROM;
var isBackgroundSearch = arguments[3];
return function (dispatch, getState) {
var state = getState();
var query = rawQuery.replace(/^lbry:\/\//i, '');
@ -4204,7 +4307,7 @@ var doSearch = exports.doSearch = function doSearch(rawQuery) {
}
// If we have already searched for something, we don't need to do anything
var urisForQuery = (0, _search2.makeSelectSearchUris)(query)(state);
var urisForQuery = (0, _search.makeSelectSearchUris)(query)(state);
if (urisForQuery && !!urisForQuery.length) {
return;
}
@ -4216,7 +4319,8 @@ var doSearch = exports.doSearch = function doSearch(rawQuery) {
// 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
if (!state.search.searchQuery) {
// isBackgroundSearch means the search is happening in the background, don't update the search query
if (!state.search.searchQuery && !isBackgroundSearch) {
dispatch({
type: ACTIONS.UPDATE_SEARCH_QUERY,
data: { searchQuery: query }
@ -4252,103 +4356,29 @@ var doSearch = exports.doSearch = function doSearch(rawQuery) {
};
var getSearchSuggestions = exports.getSearchSuggestions = function getSearchSuggestions(value /*: string*/) {
return function (dispatch) {
return function (dispatch, getState) {
var query = value.trim();
var isPrefix = function isPrefix() {
return query === '@' || query === 'lbry:' || query === 'lbry:/' || query === 'lbry://';
};
if (!query || isPrefix()) {
dispatch({
type: ACTIONS.UPDATE_SEARCH_SUGGESTIONS,
data: { suggestions: [] }
});
return;
}
var suggestions = [];
try {
// If the user is about to manually add the claim id ignore it until they
// actually add one. This would hardly ever happen, but then the search
// suggestions won't change just from adding a '#' after a uri
var uriQuery = query;
if (uriQuery.endsWith('#')) {
uriQuery = uriQuery.slice(0, -1);
}
var uri = (0, _lbryURI.normalizeURI)(uriQuery);
var _parseURI = (0, _lbryURI.parseURI)(uri),
claimName = _parseURI.claimName,
isChannel = _parseURI.isChannel;
suggestions.push({
value: uri,
shorthand: isChannel ? claimName.slice(1) : claimName,
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE
}, {
value: claimName,
type: SEARCH_TYPES.SEARCH
});
} catch (e) {
suggestions.push({
value: query,
type: SEARCH_TYPES.SEARCH
});
}
// Populate the current search query suggestion before fetching results
dispatch({
type: ACTIONS.UPDATE_SEARCH_SUGGESTIONS,
data: { suggestions: suggestions }
});
// strip out any basic stuff for more accurate search results
var searchValue = value.replace(/lbry:\/\//g, '').replace(/-/g, ' ');
var 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('#'));
}
var suggestions = (0, _search.selectSuggestions)(getState());
if (suggestions[searchValue]) {
return;
}
fetch('https://lighthouse.lbry.io/autocomplete?s=' + searchValue).then(_handleFetch2.default).then(function (apiSuggestions) {
// Suggestion could be a channel, uri, or search term
var formattedSuggestions = apiSuggestions.slice(0, 6).filter(function (suggestion) {
return suggestion !== query;
}).map(function (suggestion) {
if (suggestion.includes(' ')) {
return {
value: suggestion,
type: SEARCH_TYPES.SEARCH
};
}
try {
var _uri = (0, _lbryURI.normalizeURI)(suggestion);
var _parseURI2 = (0, _lbryURI.parseURI)(_uri),
_claimName = _parseURI2.claimName,
_isChannel = _parseURI2.isChannel;
return {
value: _uri,
shorthand: _isChannel ? _claimName.slice(1) : _claimName,
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
};
}
});
suggestions = suggestions.concat(formattedSuggestions);
dispatch({
type: ACTIONS.UPDATE_SEARCH_SUGGESTIONS,
data: { suggestions: suggestions }
data: {
query: searchValue,
suggestions: apiSuggestions
}
});
}).catch(function () {
// If the fetch fails, do nothing
@ -4387,20 +4417,6 @@ var doBlurSearchInput = exports.doBlurSearchInput = function doBlurSearchInput()
};
};
/***/ }),
/* 25 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var FILE = exports.FILE = 'file';
var CHANNEL = exports.CHANNEL = 'channel';
var SEARCH = exports.SEARCH = 'search';
/***/ }),
/* 26 */
/***/ (function(module, exports, __webpack_require__) {
@ -5903,6 +5919,7 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
/*:: type UpdateSearchSuggestions = {
type: ACTIONS.UPDATE_SEARCH_SUGGESTIONS,
data: {
query: string,
suggestions: Array<SearchSuggestion>,
},
};*/
@ -5926,7 +5943,7 @@ var 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
suggestions: [],
suggestions: {},
urisByQuery: {}
};
@ -5955,15 +5972,15 @@ var searchReducer = exports.searchReducer = (0, _reduxUtils.handleActions)((_han
});
}), _defineProperty(_handleActions, ACTIONS.UPDATE_SEARCH_SUGGESTIONS, function (state /*: SearchState*/, action /*: UpdateSearchSuggestions*/) /*: SearchState*/ {
return _extends({}, state, {
suggestions: action.data.suggestions
suggestions: _extends({}, state.suggestions, _defineProperty({}, action.data.query, action.data.suggestions))
});
}), _defineProperty(_handleActions, ACTIONS.HISTORY_NAVIGATE, function (state /*: SearchState*/, action /*: HistoryNavigate*/) /*: SearchState*/ {
var url = action.data.url;
return _extends({}, state, {
searchQuery: url.indexOf('/search') === 0 ? url.slice(14) : '',
suggestions: [],
isActive: url.indexOf('/search') === 0
isActive: url.indexOf('/search') === 0,
suggestions: {}
});
}), _defineProperty(_handleActions, ACTIONS.DISMISS_NOTIFICATION, function (state /*: SearchState*/) /*: SearchState*/ {
return _extends({}, state, {

View file

@ -189,6 +189,7 @@ export {
selectSearchUrisByQuery,
selectWunderBarAddress,
selectSearchBarFocused,
selectSearchSuggestions,
} from 'redux/selectors/search';
export {

View file

@ -1,9 +1,8 @@
// @flow
import * as ACTIONS from 'constants/action_types';
import * as SEARCH_TYPES from 'constants/search';
import { normalizeURI, buildURI, parseURI } from 'lbryURI';
import { buildURI } from 'lbryURI';
import { doResolveUri } from 'redux/actions/claims';
import { makeSelectSearchUris } from 'redux/selectors/search';
import { makeSelectSearchUris, selectSuggestions } from 'redux/selectors/search';
import { batchActions } from 'util/batchActions';
import handleFetchResponse from 'util/handle-fetch';
@ -77,102 +76,31 @@ export const doSearch = (
});
};
export const getSearchSuggestions = (value: string) => dispatch => {
export const getSearchSuggestions = (value: string) => (dispatch, getState) => {
const query = value.trim();
const isPrefix = () =>
query === '@' || query === 'lbry:' || query === 'lbry:/' || query === 'lbry://';
if (!query || isPrefix()) {
dispatch({
type: ACTIONS.UPDATE_SEARCH_SUGGESTIONS,
data: { suggestions: [] },
});
return;
}
let suggestions = [];
try {
// If the user is about to manually add the claim id ignore it until they
// actually add one. This would hardly ever happen, but then the search
// suggestions won't change just from adding a '#' after a uri
let uriQuery = query;
if (uriQuery.endsWith('#')) {
uriQuery = uriQuery.slice(0, -1);
}
const uri = normalizeURI(uriQuery);
const { claimName, isChannel } = parseURI(uri);
suggestions.push(
{
value: uri,
shorthand: isChannel ? claimName.slice(1) : claimName,
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE,
},
{
value: claimName,
type: SEARCH_TYPES.SEARCH,
}
);
} catch (e) {
suggestions.push({
value: query,
type: SEARCH_TYPES.SEARCH,
});
}
// Populate the current search query suggestion before fetching results
dispatch({
type: ACTIONS.UPDATE_SEARCH_SUGGESTIONS,
data: { suggestions },
});
// strip out any basic stuff for more accurate search results
let searchValue = value.replace(/lbry:\/\//g, '').replace(/-/g, ' ');
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(`https://lighthouse.lbry.io/autocomplete?s=${searchValue}`)
.then(handleFetchResponse)
.then(apiSuggestions => {
// Suggestion could be a channel, uri, or search term
const formattedSuggestions = apiSuggestions
.slice(0, 6)
.filter(suggestion => suggestion !== query)
.map(suggestion => {
if (suggestion.includes(' ')) {
return {
value: suggestion,
type: SEARCH_TYPES.SEARCH,
};
}
try {
const uri = normalizeURI(suggestion);
const { claimName, isChannel } = parseURI(uri);
return {
value: uri,
shorthand: isChannel ? claimName.slice(1) : claimName,
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,
};
}
});
suggestions = suggestions.concat(formattedSuggestions);
dispatch({
type: ACTIONS.UPDATE_SEARCH_SUGGESTIONS,
data: { suggestions },
data: {
query: searchValue,
suggestions: apiSuggestions,
},
});
})
.catch(() => {

View file

@ -26,6 +26,7 @@ type SearchSuggestion = {
type UpdateSearchSuggestions = {
type: ACTIONS.UPDATE_SEARCH_SUGGESTIONS,
data: {
query: string,
suggestions: Array<SearchSuggestion>,
},
};
@ -50,7 +51,7 @@ 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
suggestions: [],
suggestions: {},
urisByQuery: {},
};
@ -89,7 +90,10 @@ export const searchReducer = handleActions(
action: UpdateSearchSuggestions
): SearchState => ({
...state,
suggestions: action.data.suggestions,
suggestions: {
...state.suggestions,
[action.data.query]: action.data.suggestions,
},
}),
// clear the searchQuery on back/forward unless to search page
@ -98,8 +102,8 @@ export const searchReducer = handleActions(
return {
...state,
searchQuery: url.indexOf('/search') === 0 ? url.slice(14) : '',
suggestions: [],
isActive: url.indexOf('/search') === 0,
suggestions: {},
};
},

View file

@ -1,3 +1,5 @@
import * as SEARCH_TYPES from 'constants/search';
import { normalizeURI, parseURI } from 'lbryURI';
import { selectCurrentPage, selectCurrentParams } from 'redux/selectors/navigation';
import { createSelector } from 'reselect';
@ -5,6 +7,8 @@ export const selectState = state => state.search || {};
export const selectSearchValue = createSelector(selectState, state => state.searchQuery);
export const selectSuggestions = createSelector(selectState, state => state.suggestions);
export const selectSearchQuery = createSelector(
selectCurrentPage,
selectCurrentParams,
@ -37,3 +41,79 @@ export const selectWunderBarAddress = createSelector(
);
export const selectSearchBarFocused = createSelector(selectState, state => state.focused);
// export const selectSear
export const selectSearchSuggestions = createSelector(
selectSearchValue,
selectSuggestions,
(query, suggestions) => {
if (!query) {
return [];
}
const queryIsPrefix = query === 'lbry:' || query === 'lbry:/' || query === 'lbry://';
if (query.startsWith('lbry://') && query !== '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: SEARCH_TYPES.FILE,
},
];
} else if (queryIsPrefix) {
// If it is a prefix, wait until something else comes to figure out what to do
return [];
}
let searchSuggestions = [];
try {
const uri = normalizeURI(query);
const { claimName, isChannel } = parseURI(uri);
searchSuggestions.push(
{
value: claimName,
type: SEARCH_TYPES.SEARCH,
},
{
value: uri,
shorthand: isChannel ? claimName.slice(1) : claimName,
type: isChannel ? SEARCH_TYPES.CHANNEL : SEARCH_TYPES.FILE,
}
);
} catch (e) {
searchSuggestions.push({
value: query,
type: SEARCH_TYPES.SEARCH,
});
}
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 { claimName, isChannel } = parseURI(uri);
return {
value: uri,
shorthand: isChannel ? claimName.slice(1) : claimName,
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;
}
);