From b87e2f92f30b11942a40ddc09b5c0424360c8393 Mon Sep 17 00:00:00 2001
From: Sean Yesmunt <sean@lbry.io>
Date: Wed, 17 Jul 2019 16:50:58 -0400
Subject: [PATCH 1/8] cache claim search results by query

---
 dist/bundle.es.js                             | 124 +++++++++++-------
 src/index.js                                  |   9 +-
 src/redux/actions/claims.js                   |   7 +-
 src/redux/actions/publish.js                  |  28 ++--
 src/redux/actions/search.js                   |   2 +-
 src/redux/actions/wallet.js                   |   4 +-
 src/redux/reducers/claims.js                  |  30 +++--
 src/redux/selectors/claims.js                 |  14 +-
 src/redux/selectors/navigation.js             |  68 +++++++---
 src/redux/selectors/search.js                 |   2 +-
 .../{batchActions.js => batch-actions.js}     |   0
 src/util/claim-search.js                      |   8 ++
 .../{formatCredits.js => format-credits.js}   |   0
 src/util/{query_params.js => query-params.js} |   0
 14 files changed, 189 insertions(+), 107 deletions(-)
 rename src/util/{batchActions.js => batch-actions.js} (100%)
 create mode 100644 src/util/claim-search.js
 rename src/util/{formatCredits.js => format-credits.js} (100%)
 rename src/util/{query_params.js => query-params.js} (100%)

diff --git a/dist/bundle.es.js b/dist/bundle.es.js
index f7b480f..ec91553 100644
--- a/dist/bundle.es.js
+++ b/dist/bundle.es.js
@@ -7,6 +7,8 @@ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'defau
 require('proxy-polyfill');
 var reselect = require('reselect');
 var uuid = _interopDefault(require('uuid/v4'));
+var formatCredits$1 = require('util/formatCredits');
+require('util/batchActions');
 var fs = _interopDefault(require('fs'));
 var path = _interopDefault(require('path'));
 
@@ -1308,12 +1310,14 @@ const makeSelectClaimForUri = uri => reselect.createSelector(selectClaimsByUri,
   // Check if a claim is pending first
   // It won't be in claimsByUri because resolving it will return nothing
 
+  let valid;
   let claimId;
   try {
     ({ claimId } = parseURI(uri));
+    valid = true;
   } catch (e) {}
 
-  if (claimId) {
+  if (valid) {
     const pendingClaim = pendingById[claimId];
 
     if (pendingClaim) {
@@ -1551,7 +1555,7 @@ const makeSelectTagsForUri = uri => reselect.createSelector(makeSelectMetadataFo
 
 const selectFetchingClaimSearch = reselect.createSelector(selectState$1, state => state.fetchingClaimSearch);
 
-const selectLastClaimSearchUris = reselect.createSelector(selectState$1, state => state.lastClaimSearchUris);
+const selectClaimSearchByQuery = reselect.createSelector(selectState$1, state => state.claimSearchSearchByQuery || {});
 
 const makeSelectShortUrlForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => claim && claim.short_url);
 
@@ -1702,34 +1706,6 @@ const selectCurrentHeight = reselect.createSelector(selectState$2, state => stat
 
 const selectTransactionListFilter = reselect.createSelector(selectState$2, state => state.transactionListFilter || '');
 
-function formatCredits(amount, precision) {
-  if (Number.isNaN(parseFloat(amount))) return '0';
-  return parseFloat(amount).toFixed(precision || 1).replace(/\.?0+$/, '');
-}
-
-function formatFullPrice(amount, precision = 1) {
-  let formated = '';
-
-  const quantity = amount.toString().split('.');
-  const fraction = quantity[1];
-
-  if (fraction) {
-    const decimals = fraction.split('');
-    const first = decimals.filter(number => number !== '0')[0];
-    const index = decimals.indexOf(first);
-
-    // Set format fraction
-    formated = `.${fraction.substring(0, index + precision)}`;
-  }
-
-  return parseFloat(quantity[0] + formated);
-}
-
-function creditsToString(amount) {
-  const creditString = parseFloat(amount).toFixed(8);
-  return creditString;
-}
-
 function doUpdateBalance() {
   return (dispatch, getState) => {
     const {
@@ -1902,7 +1878,7 @@ function doSendDraftTransaction(address, amount) {
 
     lbryProxy.account_send({
       addresses: [address],
-      amount: creditsToString(amount)
+      amount: formatCredits$1.creditsToString(amount)
     }).then(successCallback, errorCallback);
   };
 }
@@ -1977,7 +1953,7 @@ function doSendTip(amount, claimId, successCallback, errorCallback) {
 
     lbryProxy.support_create({
       claim_id: claimId,
-      amount: creditsToString(amount),
+      amount: formatCredits$1.creditsToString(amount),
       tip: isSupport ? false : true
     }).then(success, error);
   };
@@ -2084,6 +2060,17 @@ function doUpdateBlockHeight() {
   });
 }
 
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+//      
+function buildClaimSearchCacheQuery(options) {
+  // Ignore page because we don't care what the last page searched was, we want everything
+  // Ignore release_time because that will change depending on when you call claim_search ex: release_time: ">12344567"
+  const rest = _objectWithoutProperties(options, ["page", "release_time"]);
+  const query = JSON.stringify(rest);
+  return query;
+}
+
 var _extends$3 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
 
 function doResolveUris(uris, returnCachedClaims = false) {
@@ -2291,7 +2278,7 @@ function doCreateChannel(name, amount) {
 
     return lbryProxy.channel_create({
       name,
-      bid: creditsToString(amount)
+      bid: formatCredits$1.creditsToString(amount)
     })
     // outputs[0] is the certificate
     // outputs[1] is the change from the tx, not in the app currently
@@ -2317,7 +2304,7 @@ function doUpdateChannel(params) {
     });
     const updateParams = {
       claim_id: params.claim_id,
-      bid: creditsToString(params.amount),
+      bid: formatCredits$1.creditsToString(params.amount),
       title: params.title,
       cover_url: params.cover,
       thumbnail_url: params.thumbnail,
@@ -2367,6 +2354,8 @@ function doFetchChannelListMine() {
 }
 
 function doClaimSearch(options = {}) {
+  const query = buildClaimSearchCacheQuery(options);
+
   return dispatch => {
     dispatch({
       type: CLAIM_SEARCH_STARTED
@@ -2382,7 +2371,7 @@ function doClaimSearch(options = {}) {
 
       dispatch({
         type: CLAIM_SEARCH_COMPLETED,
-        data: { resolveInfo, uris, append: options.page && options.page !== 1 }
+        data: { resolveInfo, uris, query, append: options.page && options.page !== 1 }
       });
     };
 
@@ -2788,12 +2777,40 @@ function batchActions(...actions) {
   };
 }
 
-function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+function formatCredits(amount, precision) {
+  if (Number.isNaN(parseFloat(amount))) return '0';
+  return parseFloat(amount).toFixed(precision || 1).replace(/\.?0+$/, '');
+}
+
+function formatFullPrice(amount, precision = 1) {
+  let formated = '';
+
+  const quantity = amount.toString().split('.');
+  const fraction = quantity[1];
+
+  if (fraction) {
+    const decimals = fraction.split('');
+    const first = decimals.filter(number => number !== '0')[0];
+    const index = decimals.indexOf(first);
+
+    // Set format fraction
+    formated = `.${fraction.substring(0, index + precision)}`;
+  }
+
+  return parseFloat(quantity[0] + formated);
+}
+
+function creditsToString(amount) {
+  const creditString = parseFloat(amount).toFixed(8);
+  return creditString;
+}
+
+function _objectWithoutProperties$1(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
 
 const selectState$5 = state => state.publish || {};
 
 const selectPublishFormValues = reselect.createSelector(selectState$5, state => {
-  const formValues = _objectWithoutProperties(state, ['pendingPublish']);
+  const formValues = _objectWithoutProperties$1(state, ['pendingPublish']);
   return formValues;
 });
 const makeSelectPublishFormValue = item => reselect.createSelector(selectState$5, state => state[item]);
@@ -3475,6 +3492,7 @@ const defaultState = {
   fetchingMyChannels: false,
   abandoningById: {},
   pendingById: {},
+  claimSearchError: undefined,
   fetchingClaimSearch: false,
   claimSearchUrisByTags: {},
   fetchingClaimSearchByTags: {},
@@ -3709,27 +3727,34 @@ reducers[RESOLVE_URIS_STARTED] = (state, action) => {
 
 reducers[CLAIM_SEARCH_STARTED] = state => {
   return Object.assign({}, state, {
-    fetchingClaimSearch: true
+    fetchingClaimSearch: true,
+    claimSearchError: false
   });
 };
-reducers[CLAIM_SEARCH_COMPLETED] = (state, action) => {
-  const { lastClaimSearchUris } = state;
 
-  let newClaimSearchUris = [];
-  if (action.data.append) {
-    newClaimSearchUris = lastClaimSearchUris.concat(action.data.uris);
+reducers[CLAIM_SEARCH_COMPLETED] = (state, action) => {
+  const { claimSearchSearchByQuery } = state;
+  const { uris, query, append } = action.data;
+
+  let newClaimSearch = _extends$5({}, claimSearchSearchByQuery);
+  if (!uris) {
+    newClaimSearch[query] = null;
+  } else if (append && newClaimSearch[query]) {
+    newClaimSearch[query] = newClaimSearch[query].concat(uris);
   } else {
-    newClaimSearchUris = action.data.uris;
+    newClaimSearch[query] = uris;
   }
 
   return _extends$5({}, handleClaimAction(state, action), {
     fetchingClaimSearch: false,
-    lastClaimSearchUris: newClaimSearchUris
+    claimSearchSearchByQuery: newClaimSearch
   });
 };
+
 reducers[CLAIM_SEARCH_FAILED] = state => {
   return Object.assign({}, state, {
-    fetchingClaimSearch: false
+    fetchingClaimSearch: false,
+    claimSearchError: true
   });
 };
 
@@ -4209,7 +4234,7 @@ const notificationsReducer = handleActions({
 
 var _extends$b = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
 
-function _objectWithoutProperties$1(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+function _objectWithoutProperties$2(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
 
 const defaultState$6 = {
   editingURI: undefined,
@@ -4259,7 +4284,7 @@ const publishReducer = handleActions({
     publishSuccess: true
   }),
   [DO_PREPARE_EDIT]: (state, action) => {
-    const publishData = _objectWithoutProperties$1(action.data, []);
+    const publishData = _objectWithoutProperties$2(action.data, []);
     const { channel, name, uri } = publishData;
 
     // The short uri is what is presented to the user
@@ -4761,6 +4786,7 @@ exports.SORT_OPTIONS = sort_options;
 exports.THUMBNAIL_STATUSES = thumbnail_upload_statuses;
 exports.TRANSACTIONS = transaction_types;
 exports.batchActions = batchActions;
+exports.buildClaimSearchCacheQuery = buildClaimSearchCacheQuery;
 exports.buildURI = buildURI;
 exports.claimsReducer = claimsReducer;
 exports.commentReducer = commentReducer;
@@ -4883,6 +4909,7 @@ exports.selectAllMyClaimsByOutpoint = selectAllMyClaimsByOutpoint;
 exports.selectBalance = selectBalance;
 exports.selectBlocks = selectBlocks;
 exports.selectChannelClaimCounts = selectChannelClaimCounts;
+exports.selectClaimSearchByQuery = selectClaimSearchByQuery;
 exports.selectClaimSearchUrisByTags = selectClaimSearchUrisByTags;
 exports.selectClaimsById = selectClaimsById;
 exports.selectClaimsByUri = selectClaimsByUri;
@@ -4914,7 +4941,6 @@ exports.selectIsResolvingPublishUris = selectIsResolvingPublishUris;
 exports.selectIsSearching = selectIsSearching;
 exports.selectIsSendingSupport = selectIsSendingSupport;
 exports.selectIsStillEditing = selectIsStillEditing;
-exports.selectLastClaimSearchUris = selectLastClaimSearchUris;
 exports.selectLastPurchasedUri = selectLastPurchasedUri;
 exports.selectMyActiveClaims = selectMyActiveClaims;
 exports.selectMyChannelClaims = selectMyChannelClaims;
diff --git a/src/index.js b/src/index.js
index 07a30dd..a8117ec 100644
--- a/src/index.js
+++ b/src/index.js
@@ -113,10 +113,11 @@ export { doToggleTagFollow, doAddTag, doDeleteTag } from 'redux/actions/tags';
 export { doCommentList, doCommentCreate } from 'redux/actions/comments';
 
 // utils
-export { batchActions } from 'util/batchActions';
-export { parseQueryParams, toQueryString } from 'util/query_params';
-export { formatCredits, formatFullPrice, creditsToString } from 'util/formatCredits';
+export { batchActions } from 'util/batch-actions';
+export { parseQueryParams, toQueryString } from 'util/query-params';
+export { formatCredits, formatFullPrice, creditsToString } from 'util/format-credits';
 export { isClaimNsfw } from 'util/claim';
+export { buildClaimSearchCacheQuery } from 'util/claim-search';
 
 // reducers
 export { claimsReducer } from 'redux/reducers/claims';
@@ -193,11 +194,11 @@ export {
   selectChannelClaimCounts,
   selectCurrentChannelPage,
   selectFetchingClaimSearch,
-  selectLastClaimSearchUris,
   selectFetchingClaimSearchByTags,
   selectClaimSearchUrisByTags,
   makeSelectFetchingClaimSearchForTags,
   makeSelectClaimSearchUrisForTags,
+  selectClaimSearchByQuery,
 } from 'redux/selectors/claims';
 
 export { makeSelectCommentsForUri } from 'redux/selectors/comments';
diff --git a/src/redux/actions/claims.js b/src/redux/actions/claims.js
index 34e1be3..49680e7 100644
--- a/src/redux/actions/claims.js
+++ b/src/redux/actions/claims.js
@@ -9,6 +9,7 @@ import { selectSupportsByOutpoint } from 'redux/selectors/wallet';
 import { creditsToString } from 'util/formatCredits';
 import { batchActions } from 'util/batchActions';
 import { createNormalizedTagKey } from 'util/claim';
+import { buildClaimSearchCacheQuery } from 'util/claim-search';
 
 export function doResolveUris(uris: Array<string>, returnCachedClaims: boolean = false) {
   return (dispatch: Dispatch, getState: GetState) => {
@@ -314,7 +315,9 @@ export function doFetchChannelListMine() {
   };
 }
 
-export function doClaimSearch(options: { page_size?: number, page?: number } = {}) {
+export function doClaimSearch(options: { page?: number, release_time?: string } = {}) {
+  const query = buildClaimSearchCacheQuery(options);
+
   return (dispatch: Dispatch) => {
     dispatch({
       type: ACTIONS.CLAIM_SEARCH_STARTED,
@@ -330,7 +333,7 @@ export function doClaimSearch(options: { page_size?: number, page?: number } = {
 
       dispatch({
         type: ACTIONS.CLAIM_SEARCH_COMPLETED,
-        data: { resolveInfo, uris, append: options.page && options.page !== 1 },
+        data: { resolveInfo, uris, query, append: options.page && options.page !== 1 },
       });
     };
 
diff --git a/src/redux/actions/publish.js b/src/redux/actions/publish.js
index 95b2229..38cb9bd 100644
--- a/src/redux/actions/publish.js
+++ b/src/redux/actions/publish.js
@@ -3,8 +3,8 @@ import { CC_LICENSES, COPYRIGHT, OTHER, NONE, PUBLIC_DOMAIN } from 'constants/li
 import * as ACTIONS from 'constants/action_types';
 import * as THUMBNAIL_STATUSES from 'constants/thumbnail_upload_statuses';
 import Lbry from 'lbry';
-import { batchActions } from 'util/batchActions';
-import { creditsToString } from 'util/formatCredits';
+import { batchActions } from 'util/batch-actions';
+import { creditsToString } from 'util/format-credits';
 import { doError } from 'redux/actions/notifications';
 import { isClaimNsfw } from 'util/claim';
 import {
@@ -118,12 +118,12 @@ export const doUploadThumbnail = (
         .then(json =>
           json.success
             ? dispatch({
-              type: ACTIONS.UPDATE_PUBLISH_FORM,
-              data: {
-                uploadThumbnailStatus: THUMBNAIL_STATUSES.COMPLETE,
-                thumbnail: `${json.data.url}${fileExt}`,
-              },
-            })
+                type: ACTIONS.UPDATE_PUBLISH_FORM,
+                data: {
+                  uploadThumbnailStatus: THUMBNAIL_STATUSES.COMPLETE,
+                  thumbnail: `${json.data.url}${fileExt}`,
+                },
+              })
             : uploadError(json.message)
         )
         .catch(err => uploadError(err.message));
@@ -157,12 +157,12 @@ export const doUploadThumbnail = (
       .then(json =>
         json.success
           ? dispatch({
-            type: ACTIONS.UPDATE_PUBLISH_FORM,
-            data: {
-              uploadThumbnailStatus: THUMBNAIL_STATUSES.COMPLETE,
-              thumbnail: `${json.data.url}${fileExt}`,
-            },
-          })
+              type: ACTIONS.UPDATE_PUBLISH_FORM,
+              data: {
+                uploadThumbnailStatus: THUMBNAIL_STATUSES.COMPLETE,
+                thumbnail: `${json.data.url}${fileExt}`,
+              },
+            })
           : uploadError(json.message)
       )
       .catch(err => uploadError(err.message));
diff --git a/src/redux/actions/search.js b/src/redux/actions/search.js
index 6ce900e..8108e9c 100644
--- a/src/redux/actions/search.js
+++ b/src/redux/actions/search.js
@@ -8,7 +8,7 @@ import {
   makeSelectQueryWithOptions,
   selectSearchValue,
 } from 'redux/selectors/search';
-import { batchActions } from 'util/batchActions';
+import { batchActions } from 'util/batch-actions';
 import debounce from 'util/debounce';
 import handleFetchResponse from 'util/handle-fetch';
 
diff --git a/src/redux/actions/wallet.js b/src/redux/actions/wallet.js
index 6e5dce6..73d9e98 100644
--- a/src/redux/actions/wallet.js
+++ b/src/redux/actions/wallet.js
@@ -230,7 +230,9 @@ export function doSendTip(amount, claimId, successCallback, errorCallback) {
     const success = () => {
       dispatch(
         doToast({
-          message: isSupport ? __(`You deposited ${amount} LBC as a support!`) : __(`You sent ${amount} LBC as a tip, Mahalo!`),
+          message: isSupport
+            ? __(`You deposited ${amount} LBC as a support!`)
+            : __(`You sent ${amount} LBC as a tip, Mahalo!`),
           linkText: __('History'),
           linkTarget: __('/wallet'),
         })
diff --git a/src/redux/reducers/claims.js b/src/redux/reducers/claims.js
index 1681f76..e317af7 100644
--- a/src/redux/reducers/claims.js
+++ b/src/redux/reducers/claims.js
@@ -46,6 +46,7 @@ const defaultState = {
   fetchingMyChannels: false,
   abandoningById: {},
   pendingById: {},
+  claimSearchError: undefined,
   fetchingClaimSearch: false,
   claimSearchUrisByTags: {},
   fetchingClaimSearchByTags: {},
@@ -292,27 +293,34 @@ reducers[ACTIONS.RESOLVE_URIS_STARTED] = (state: State, action: any): State => {
 reducers[ACTIONS.CLAIM_SEARCH_STARTED] = (state: State): State => {
   return Object.assign({}, state, {
     fetchingClaimSearch: true,
+    claimSearchError: false,
   });
 };
-reducers[ACTIONS.CLAIM_SEARCH_COMPLETED] = (state: State, action: any): State => {
-  const { lastClaimSearchUris } = state;
 
-  let newClaimSearchUris = [];
-  if (action.data.append) {
-    newClaimSearchUris = lastClaimSearchUris.concat(action.data.uris);
+reducers[ACTIONS.CLAIM_SEARCH_COMPLETED] = (state: State, action: any): State => {
+  const { claimSearchSearchByQuery } = state;
+  const { uris, query, append } = action.data;
+
+  let newClaimSearch = { ...claimSearchSearchByQuery };
+  if (!uris) {
+    newClaimSearch[query] = null;
+  } else if (append && newClaimSearch[query]) {
+    newClaimSearch[query] = newClaimSearch[query].concat(uris);
   } else {
-    newClaimSearchUris = action.data.uris;
+    newClaimSearch[query] = uris;
   }
 
   return {
     ...handleClaimAction(state, action),
     fetchingClaimSearch: false,
-    lastClaimSearchUris: newClaimSearchUris,
+    claimSearchSearchByQuery: newClaimSearch,
   };
 };
+
 reducers[ACTIONS.CLAIM_SEARCH_FAILED] = (state: State): State => {
   return Object.assign({}, state, {
     fetchingClaimSearch: false,
+    claimSearchError: true,
   });
 };
 
@@ -321,7 +329,7 @@ reducers[ACTIONS.CLAIM_SEARCH_BY_TAGS_STARTED] = (state: State, action: any): St
   fetchingClaimSearchByTags[action.data.tags] = true;
 
   return Object.assign({}, state, {
-    fetchingClaimSearchByTags
+    fetchingClaimSearchByTags,
   });
 };
 reducers[ACTIONS.CLAIM_SEARCH_BY_TAGS_COMPLETED] = (state: State, action: any): State => {
@@ -331,8 +339,10 @@ reducers[ACTIONS.CLAIM_SEARCH_BY_TAGS_COMPLETED] = (state: State, action: any):
 
   if (action.data.append) {
     // todo: check for duplicate uris when concatenating?
-    claimSearchUrisByTags[tags] = claimSearchUrisByTags[tags] && claimSearchUrisByTags[tags].length ?
-      claimSearchUrisByTags[tags].concat(uris) : uris;
+    claimSearchUrisByTags[tags] =
+      claimSearchUrisByTags[tags] && claimSearchUrisByTags[tags].length
+        ? claimSearchUrisByTags[tags].concat(uris)
+        : uris;
   } else {
     claimSearchUrisByTags[tags] = uris;
   }
diff --git a/src/redux/selectors/claims.js b/src/redux/selectors/claims.js
index 2019e18..fee7b2a 100644
--- a/src/redux/selectors/claims.js
+++ b/src/redux/selectors/claims.js
@@ -3,7 +3,7 @@ import { normalizeURI, buildURI, parseURI } from 'lbryURI';
 import { selectSearchUrisByQuery } from 'redux/selectors/search';
 import { createSelector } from 'reselect';
 import { isClaimNsfw, createNormalizedTagKey } from 'util/claim';
-import { getSearchQueryString } from 'util/query_params';
+import { getSearchQueryString } from 'util/query-params';
 
 const selectState = state => state.claims || {};
 
@@ -88,12 +88,14 @@ export const makeSelectClaimForUri = (uri: string) =>
       // Check if a claim is pending first
       // It won't be in claimsByUri because resolving it will return nothing
 
+      let valid;
       let claimId;
       try {
         ({ claimId } = parseURI(uri));
+        valid = true;
       } catch (e) {}
 
-      if (claimId) {
+      if (valid) {
         const pendingClaim = pendingById[claimId];
 
         if (pendingClaim) {
@@ -253,7 +255,9 @@ export const makeSelectThumbnailForUri = (uri: string) =>
     makeSelectClaimForUri(uri),
     claim => {
       const thumbnail = claim && claim.value && claim.value.thumbnail;
-      return thumbnail && thumbnail.url && thumbnail.url.trim().length > 0 ? thumbnail.url : undefined;
+      return thumbnail && thumbnail.url && thumbnail.url.trim().length > 0
+        ? thumbnail.url
+        : undefined;
     }
   );
 
@@ -499,9 +503,9 @@ export const selectFetchingClaimSearch = createSelector(
   state => state.fetchingClaimSearch
 );
 
-export const selectLastClaimSearchUris = createSelector(
+export const selectClaimSearchByQuery = createSelector(
   selectState,
-  state => state.lastClaimSearchUris
+  state => state.claimSearchSearchByQuery || {}
 );
 
 export const makeSelectShortUrlForUri = (uri: string) =>
diff --git a/src/redux/selectors/navigation.js b/src/redux/selectors/navigation.js
index 8aafd3c..3b81d79 100644
--- a/src/redux/selectors/navigation.js
+++ b/src/redux/selectors/navigation.js
@@ -1,40 +1,65 @@
 import { createSelector } from 'reselect';
-import { parseQueryParams } from 'util/query_params';
+import { parseQueryParams } from 'util/query-params';
 
 export const selectState = state => state.navigation || {};
 
-export const selectCurrentPath = createSelector(selectState, state => state.currentPath);
+export const selectCurrentPath = createSelector(
+  selectState,
+  state => state.currentPath
+);
 
 export const computePageFromPath = path => (path ? path.replace(/^\//, '').split('?')[0] : '');
 
-export const selectCurrentPage = createSelector(selectCurrentPath, path =>
-  computePageFromPath(path)
+export const selectCurrentPage = createSelector(
+  selectCurrentPath,
+  path => computePageFromPath(path)
 );
 
-export const selectCurrentParams = createSelector(selectCurrentPath, path => {
-  if (path === undefined) return {};
-  if (!path.match(/\?/)) return {};
+export const selectCurrentParams = createSelector(
+  selectCurrentPath,
+  path => {
+    if (path === undefined) return {};
+    if (!path.match(/\?/)) return {};
 
-  return parseQueryParams(path.split('?')[1]);
-});
+    return parseQueryParams(path.split('?')[1]);
+  }
+);
 
 export const makeSelectCurrentParam = param =>
-  createSelector(selectCurrentParams, params => (params ? params[param] : undefined));
+  createSelector(
+    selectCurrentParams,
+    params => (params ? params[param] : undefined)
+  );
 
-export const selectPathAfterAuth = createSelector(selectState, state => state.pathAfterAuth);
+export const selectPathAfterAuth = createSelector(
+  selectState,
+  state => state.pathAfterAuth
+);
 
-export const selectIsBackDisabled = createSelector(selectState, state => state.index === 0);
+export const selectIsBackDisabled = createSelector(
+  selectState,
+  state => state.index === 0
+);
 
 export const selectIsForwardDisabled = createSelector(
   selectState,
   state => state.index === state.stack.length - 1
 );
 
-export const selectIsHome = createSelector(selectCurrentPage, page => page === 'discover');
+export const selectIsHome = createSelector(
+  selectCurrentPage,
+  page => page === 'discover'
+);
 
-export const selectHistoryIndex = createSelector(selectState, state => state.index);
+export const selectHistoryIndex = createSelector(
+  selectState,
+  state => state.index
+);
 
-export const selectHistoryStack = createSelector(selectState, state => state.stack);
+export const selectHistoryStack = createSelector(
+  selectState,
+  state => state.stack
+);
 
 // returns current page attributes (scrollY, path)
 export const selectActiveHistoryEntry = createSelector(
@@ -42,9 +67,12 @@ export const selectActiveHistoryEntry = createSelector(
   state => state.stack[state.index]
 );
 
-export const selectPageTitle = createSelector(selectCurrentPage, page => {
-  switch (page) {
-    default:
-      return '';
+export const selectPageTitle = createSelector(
+  selectCurrentPage,
+  page => {
+    switch (page) {
+      default:
+        return '';
+    }
   }
-});
+);
diff --git a/src/redux/selectors/search.js b/src/redux/selectors/search.js
index 152a99e..660228f 100644
--- a/src/redux/selectors/search.js
+++ b/src/redux/selectors/search.js
@@ -1,6 +1,6 @@
 // @flow
 import { SEARCH_TYPES, SEARCH_OPTIONS } from 'constants/search';
-import { getSearchQueryString } from 'util/query_params';
+import { getSearchQueryString } from 'util/query-params';
 import { normalizeURI, parseURI } from 'lbryURI';
 import { createSelector } from 'reselect';
 
diff --git a/src/util/batchActions.js b/src/util/batch-actions.js
similarity index 100%
rename from src/util/batchActions.js
rename to src/util/batch-actions.js
diff --git a/src/util/claim-search.js b/src/util/claim-search.js
new file mode 100644
index 0000000..44913cf
--- /dev/null
+++ b/src/util/claim-search.js
@@ -0,0 +1,8 @@
+// @flow
+export function buildClaimSearchCacheQuery(options: { page?: number, release_time?: string }) {
+  // Ignore page because we don't care what the last page searched was, we want everything
+  // Ignore release_time because that will change depending on when you call claim_search ex: release_time: ">12344567"
+  const { page: optionToIgnoreForQuery, release_time: anotherToIgnore, ...rest } = options;
+  const query = JSON.stringify(rest);
+  return query;
+}
diff --git a/src/util/formatCredits.js b/src/util/format-credits.js
similarity index 100%
rename from src/util/formatCredits.js
rename to src/util/format-credits.js
diff --git a/src/util/query_params.js b/src/util/query-params.js
similarity index 100%
rename from src/util/query_params.js
rename to src/util/query-params.js

From 3e7b5d05c056dd831c04d848c0b71c983c36b8c3 Mon Sep 17 00:00:00 2001
From: Sean Yesmunt <sean@lbry.io>
Date: Wed, 17 Jul 2019 17:34:00 -0400
Subject: [PATCH 2/8] fix: invalid uri characters

---
 dist/bundle.es.js            | 4 ++--
 src/lbryURI.js               | 2 +-
 src/redux/reducers/claims.js | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/dist/bundle.es.js b/dist/bundle.es.js
index ec91553..1e9168b 100644
--- a/dist/bundle.es.js
+++ b/dist/bundle.es.js
@@ -904,7 +904,7 @@ const channelNameMinLength = 1;
 const claimIdMaxLength = 40;
 
 // see https://spec.lbry.com/#urls
-const regexInvalidURI = /[ =&#:$@%?\u{0000}-\u{0008}\u{000b}-\u{000c}\u{000e}-\u{001F}\u{D800}-\u{DFFF}\u{FFFE}-\u{FFFF}]/gu;
+const regexInvalidURI = /[ =&#:$@%?;/\\"<>%{}|^~[\]`\u{0000}-\u{0008}\u{000b}-\u{000c}\u{000e}-\u{001F}\u{D800}-\u{DFFF}\u{FFFE}-\u{FFFF}]/gu;
 const regexAddress = /^(b|r)(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
 
 /**
@@ -3492,7 +3492,7 @@ const defaultState = {
   fetchingMyChannels: false,
   abandoningById: {},
   pendingById: {},
-  claimSearchError: undefined,
+  claimSearchError: false,
   fetchingClaimSearch: false,
   claimSearchUrisByTags: {},
   fetchingClaimSearchByTags: {},
diff --git a/src/lbryURI.js b/src/lbryURI.js
index 42045d0..32c6ce6 100644
--- a/src/lbryURI.js
+++ b/src/lbryURI.js
@@ -2,7 +2,7 @@ const channelNameMinLength = 1;
 const claimIdMaxLength = 40;
 
 // see https://spec.lbry.com/#urls
-export const regexInvalidURI = /[ =&#:$@%?\u{0000}-\u{0008}\u{000b}-\u{000c}\u{000e}-\u{001F}\u{D800}-\u{DFFF}\u{FFFE}-\u{FFFF}]/gu;
+export const regexInvalidURI = /[ =&#:$@%?;/\\"<>%{}|^~[\]`\u{0000}-\u{0008}\u{000b}-\u{000c}\u{000e}-\u{001F}\u{D800}-\u{DFFF}\u{FFFE}-\u{FFFF}]/gu;
 export const regexAddress = /^(b|r)(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
 
 /**
diff --git a/src/redux/reducers/claims.js b/src/redux/reducers/claims.js
index e317af7..1ef4c40 100644
--- a/src/redux/reducers/claims.js
+++ b/src/redux/reducers/claims.js
@@ -46,7 +46,7 @@ const defaultState = {
   fetchingMyChannels: false,
   abandoningById: {},
   pendingById: {},
-  claimSearchError: undefined,
+  claimSearchError: false,
   fetchingClaimSearch: false,
   claimSearchUrisByTags: {},
   fetchingClaimSearchByTags: {},

From 8910693fe1fc4166fdc748d128344012c3d61874 Mon Sep 17 00:00:00 2001
From: Sean Yesmunt <sean@lbry.io>
Date: Mon, 29 Jul 2019 14:48:44 -0400
Subject: [PATCH 3/8] fix typo

---
 dist/bundle.es.js           | 82 ++++++++++++++++++-------------------
 src/redux/actions/claims.js |  4 +-
 src/redux/actions/wallet.js |  2 +-
 3 files changed, 43 insertions(+), 45 deletions(-)

diff --git a/dist/bundle.es.js b/dist/bundle.es.js
index 1e9168b..9c37e36 100644
--- a/dist/bundle.es.js
+++ b/dist/bundle.es.js
@@ -7,8 +7,6 @@ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'defau
 require('proxy-polyfill');
 var reselect = require('reselect');
 var uuid = _interopDefault(require('uuid/v4'));
-var formatCredits$1 = require('util/formatCredits');
-require('util/batchActions');
 var fs = _interopDefault(require('fs'));
 var path = _interopDefault(require('path'));
 
@@ -1706,6 +1704,34 @@ const selectCurrentHeight = reselect.createSelector(selectState$2, state => stat
 
 const selectTransactionListFilter = reselect.createSelector(selectState$2, state => state.transactionListFilter || '');
 
+function formatCredits(amount, precision) {
+  if (Number.isNaN(parseFloat(amount))) return '0';
+  return parseFloat(amount).toFixed(precision || 1).replace(/\.?0+$/, '');
+}
+
+function formatFullPrice(amount, precision = 1) {
+  let formated = '';
+
+  const quantity = amount.toString().split('.');
+  const fraction = quantity[1];
+
+  if (fraction) {
+    const decimals = fraction.split('');
+    const first = decimals.filter(number => number !== '0')[0];
+    const index = decimals.indexOf(first);
+
+    // Set format fraction
+    formated = `.${fraction.substring(0, index + precision)}`;
+  }
+
+  return parseFloat(quantity[0] + formated);
+}
+
+function creditsToString(amount) {
+  const creditString = parseFloat(amount).toFixed(8);
+  return creditString;
+}
+
 function doUpdateBalance() {
   return (dispatch, getState) => {
     const {
@@ -1878,7 +1904,7 @@ function doSendDraftTransaction(address, amount) {
 
     lbryProxy.account_send({
       addresses: [address],
-      amount: formatCredits$1.creditsToString(amount)
+      amount: creditsToString(amount)
     }).then(successCallback, errorCallback);
   };
 }
@@ -1953,7 +1979,7 @@ function doSendTip(amount, claimId, successCallback, errorCallback) {
 
     lbryProxy.support_create({
       claim_id: claimId,
-      amount: formatCredits$1.creditsToString(amount),
+      amount: creditsToString(amount),
       tip: isSupport ? false : true
     }).then(success, error);
   };
@@ -2060,6 +2086,14 @@ function doUpdateBlockHeight() {
   });
 }
 
+// https://github.com/reactjs/redux/issues/911
+function batchActions(...actions) {
+  return {
+    type: 'BATCH_ACTIONS',
+    actions
+  };
+}
+
 function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
 
 //      
@@ -2278,7 +2312,7 @@ function doCreateChannel(name, amount) {
 
     return lbryProxy.channel_create({
       name,
-      bid: formatCredits$1.creditsToString(amount)
+      bid: creditsToString(amount)
     })
     // outputs[0] is the certificate
     // outputs[1] is the change from the tx, not in the app currently
@@ -2304,7 +2338,7 @@ function doUpdateChannel(params) {
     });
     const updateParams = {
       claim_id: params.claim_id,
-      bid: formatCredits$1.creditsToString(params.amount),
+      bid: creditsToString(params.amount),
       title: params.title,
       cover_url: params.cover,
       thumbnail_url: params.thumbnail,
@@ -2769,42 +2803,6 @@ function doSetFileListSort(page, value) {
   };
 }
 
-// https://github.com/reactjs/redux/issues/911
-function batchActions(...actions) {
-  return {
-    type: 'BATCH_ACTIONS',
-    actions
-  };
-}
-
-function formatCredits(amount, precision) {
-  if (Number.isNaN(parseFloat(amount))) return '0';
-  return parseFloat(amount).toFixed(precision || 1).replace(/\.?0+$/, '');
-}
-
-function formatFullPrice(amount, precision = 1) {
-  let formated = '';
-
-  const quantity = amount.toString().split('.');
-  const fraction = quantity[1];
-
-  if (fraction) {
-    const decimals = fraction.split('');
-    const first = decimals.filter(number => number !== '0')[0];
-    const index = decimals.indexOf(first);
-
-    // Set format fraction
-    formated = `.${fraction.substring(0, index + precision)}`;
-  }
-
-  return parseFloat(quantity[0] + formated);
-}
-
-function creditsToString(amount) {
-  const creditString = parseFloat(amount).toFixed(8);
-  return creditString;
-}
-
 function _objectWithoutProperties$1(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
 
 const selectState$5 = state => state.publish || {};
diff --git a/src/redux/actions/claims.js b/src/redux/actions/claims.js
index 49680e7..20a9a05 100644
--- a/src/redux/actions/claims.js
+++ b/src/redux/actions/claims.js
@@ -6,8 +6,8 @@ import { doToast } from 'redux/actions/notifications';
 import { selectMyClaimsRaw, selectResolvingUris, selectClaimsByUri } from 'redux/selectors/claims';
 import { doFetchTransactions } from 'redux/actions/wallet';
 import { selectSupportsByOutpoint } from 'redux/selectors/wallet';
-import { creditsToString } from 'util/formatCredits';
-import { batchActions } from 'util/batchActions';
+import { creditsToString } from 'util/format-credits';
+import { batchActions } from 'util/batch-actions';
 import { createNormalizedTagKey } from 'util/claim';
 import { buildClaimSearchCacheQuery } from 'util/claim-search';
 
diff --git a/src/redux/actions/wallet.js b/src/redux/actions/wallet.js
index 73d9e98..00865bd 100644
--- a/src/redux/actions/wallet.js
+++ b/src/redux/actions/wallet.js
@@ -2,7 +2,7 @@ import * as ACTIONS from 'constants/action_types';
 import Lbry from 'lbry';
 import { doToast } from 'redux/actions/notifications';
 import { selectBalance } from 'redux/selectors/wallet';
-import { creditsToString } from 'util/formatCredits';
+import { creditsToString } from 'util/format-credits';
 import { selectMyClaimsRaw } from 'redux/selectors/claims';
 
 export function doUpdateBalance() {

From a1ae63d0b6c048248aa5615f814601616df15031 Mon Sep 17 00:00:00 2001
From: Sean Yesmunt <sean@lbry.io>
Date: Tue, 30 Jul 2019 11:48:45 -0400
Subject: [PATCH 4/8] handle all options for claim_search cache queries

---
 dist/bundle.es.js             | 176 ++++++++++------------------------
 src/index.js                  |   9 +-
 src/redux/actions/claims.js   |  63 +++---------
 src/redux/reducers/claims.js  |  87 ++++++-----------
 src/redux/selectors/claims.js |  50 +++++-----
 src/util/claim-search.js      |   8 --
 src/util/claim.js             |  10 +-
 7 files changed, 120 insertions(+), 283 deletions(-)
 delete mode 100644 src/util/claim-search.js

diff --git a/dist/bundle.es.js b/dist/bundle.es.js
index 9c37e36..e0a8de9 100644
--- a/dist/bundle.es.js
+++ b/dist/bundle.es.js
@@ -1228,6 +1228,8 @@ function doDismissError() {
 
 var _extends$2 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
 
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
 const matureTagMap = MATURE_TAGS.reduce((acc, tag) => _extends$2({}, acc, { [tag]: true }), {});
 
 const isClaimNsfw = claim => {
@@ -1250,9 +1252,13 @@ const isClaimNsfw = claim => {
   return false;
 };
 
-const createNormalizedTagKey = tags => {
-  return tags ? tags.sort().join(',') : '';
-};
+function createNormalizedClaimSearchKey(options) {
+  // Ignore page because we don't care what the last page searched was, we want everything
+  // Ignore release_time because that will change depending on when you call claim_search ex: release_time: ">12344567"
+  const rest = _objectWithoutProperties(options, ['page', 'release_time']);
+  const query = JSON.stringify(rest);
+  return query;
+}
 
 //      
 
@@ -1551,19 +1557,17 @@ const makeSelectTagsForUri = uri => reselect.createSelector(makeSelectMetadataFo
   return metadata && metadata.tags || [];
 });
 
-const selectFetchingClaimSearch = reselect.createSelector(selectState$1, state => state.fetchingClaimSearch);
+const selectfetchingClaimSearchByQuery = reselect.createSelector(selectState$1, state => state.fetchingClaimSearchByQuery || {});
 
-const selectClaimSearchByQuery = reselect.createSelector(selectState$1, state => state.claimSearchSearchByQuery || {});
+const selectFetchingClaimSearch = reselect.createSelector(selectfetchingClaimSearchByQuery, fetchingClaimSearchByQuery => Boolean(Object.keys(fetchingClaimSearchByQuery).length));
+
+const selectClaimSearchByQuery = reselect.createSelector(selectState$1, state => state.claimSearchByQuery || {});
 
 const makeSelectShortUrlForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => claim && claim.short_url);
 
-const selectFetchingClaimSearchByTags = reselect.createSelector(selectState$1, state => state.fetchingClaimSearchByTags);
+const makeSelectFetchingClaimSearchForTags = tags => reselect.createSelector(selectfetchingClaimSearchByQuery, byQuery => byQuery[createNormalizedClaimSearchKey({ any_tags: tags })]);
 
-const selectClaimSearchUrisByTags = reselect.createSelector(selectState$1, state => state.claimSearchUrisByTags);
-
-const makeSelectFetchingClaimSearchForTags = tags => reselect.createSelector(selectFetchingClaimSearchByTags, byTags => byTags[createNormalizedTagKey(tags)]);
-
-const makeSelectClaimSearchUrisForTags = tags => reselect.createSelector(selectClaimSearchUrisByTags, byTags => byTags[createNormalizedTagKey(tags)]);
+const makeSelectClaimSearchUrisForTags = tags => reselect.createSelector(selectClaimSearchByQuery, byQuery => byQuery[createNormalizedClaimSearchKey({ any_tags: tags })]);
 
 const selectState$2 = state => state.wallet || {};
 
@@ -2094,17 +2098,6 @@ function batchActions(...actions) {
   };
 }
 
-function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
-
-//      
-function buildClaimSearchCacheQuery(options) {
-  // Ignore page because we don't care what the last page searched was, we want everything
-  // Ignore release_time because that will change depending on when you call claim_search ex: release_time: ">12344567"
-  const rest = _objectWithoutProperties(options, ["page", "release_time"]);
-  const query = JSON.stringify(rest);
-  return query;
-}
-
 var _extends$3 = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
 
 function doResolveUris(uris, returnCachedClaims = false) {
@@ -2387,12 +2380,14 @@ function doFetchChannelListMine() {
   };
 }
 
-function doClaimSearch(options = {}) {
-  const query = buildClaimSearchCacheQuery(options);
-
+function doClaimSearch(options = {
+  page_size: 10
+}) {
+  const query = createNormalizedClaimSearchKey(options);
   return dispatch => {
     dispatch({
-      type: CLAIM_SEARCH_STARTED
+      type: CLAIM_SEARCH_STARTED,
+      data: { query: query }
     });
 
     const success = data => {
@@ -2405,56 +2400,19 @@ function doClaimSearch(options = {}) {
 
       dispatch({
         type: CLAIM_SEARCH_COMPLETED,
-        data: { resolveInfo, uris, query, append: options.page && options.page !== 1 }
+        data: { query, resolveInfo, uris, append: options.page && options.page !== 1 }
       });
     };
 
     const failure = err => {
       dispatch({
         type: CLAIM_SEARCH_FAILED,
+        data: { query },
         error: err
       });
     };
 
-    lbryProxy.claim_search(_extends$3({}, options)).then(success, failure);
-  };
-}
-
-// tags can be one or many (comma separated)
-function doClaimSearchByTags(tags, amount = 10, options = {}) {
-  return dispatch => {
-    const tagList = createNormalizedTagKey(tags);
-    dispatch({
-      type: CLAIM_SEARCH_BY_TAGS_STARTED,
-      data: { tags: tagList }
-    });
-
-    const success = data => {
-      const resolveInfo = {};
-      const uris = [];
-      data.items.forEach(stream => {
-        resolveInfo[stream.permanent_url] = { stream };
-        uris.push(stream.permanent_url);
-      });
-
-      dispatch({
-        type: CLAIM_SEARCH_BY_TAGS_COMPLETED,
-        data: { tags: tagList, resolveInfo, uris, append: options.page && options.page !== 1 }
-      });
-    };
-
-    const failure = err => {
-      dispatch({
-        type: CLAIM_SEARCH_BY_TAGS_FAILED,
-        data: { tags: tagList },
-        error: err
-      });
-    };
-
-    lbryProxy.claim_search(_extends$3({
-      page_size: amount,
-      any_tags: tags
-    }, options)).then(success, failure);
+    lbryProxy.claim_search(options).then(success, failure);
   };
 }
 
@@ -3491,10 +3449,8 @@ const defaultState = {
   abandoningById: {},
   pendingById: {},
   claimSearchError: false,
-  fetchingClaimSearch: false,
-  claimSearchUrisByTags: {},
-  fetchingClaimSearchByTags: {},
-  lastClaimSearchUris: []
+  claimSearchByQuery: {},
+  fetchingClaimSearchByQuery: {}
 };
 
 function handleClaimAction(state, action) {
@@ -3723,71 +3679,41 @@ reducers[RESOLVE_URIS_STARTED] = (state, action) => {
   });
 };
 
-reducers[CLAIM_SEARCH_STARTED] = state => {
+reducers[CLAIM_SEARCH_STARTED] = (state, action) => {
+  const fetchingClaimSearchByQuery = Object.assign({}, state.fetchingClaimSearchByQuery);
+  fetchingClaimSearchByQuery[action.data.query] = true;
+
   return Object.assign({}, state, {
-    fetchingClaimSearch: true,
-    claimSearchError: false
+    fetchingClaimSearchByQuery
   });
 };
 
 reducers[CLAIM_SEARCH_COMPLETED] = (state, action) => {
-  const { claimSearchSearchByQuery } = state;
-  const { uris, query, append } = action.data;
+  const fetchingClaimSearchByQuery = Object.assign({}, state.fetchingClaimSearchByQuery);
+  const claimSearchByQuery = Object.assign({}, state.claimSearchByQuery);
+  const { append, query, uris } = action.data;
 
-  let newClaimSearch = _extends$5({}, claimSearchSearchByQuery);
-  if (!uris) {
-    newClaimSearch[query] = null;
-  } else if (append && newClaimSearch[query]) {
-    newClaimSearch[query] = newClaimSearch[query].concat(uris);
-  } else {
-    newClaimSearch[query] = uris;
-  }
-
-  return _extends$5({}, handleClaimAction(state, action), {
-    fetchingClaimSearch: false,
-    claimSearchSearchByQuery: newClaimSearch
-  });
-};
-
-reducers[CLAIM_SEARCH_FAILED] = state => {
-  return Object.assign({}, state, {
-    fetchingClaimSearch: false,
-    claimSearchError: true
-  });
-};
-
-reducers[CLAIM_SEARCH_BY_TAGS_STARTED] = (state, action) => {
-  const fetchingClaimSearchByTags = Object.assign({}, state.fetchingClaimSearchByTags);
-  fetchingClaimSearchByTags[action.data.tags] = true;
-
-  return Object.assign({}, state, {
-    fetchingClaimSearchByTags
-  });
-};
-reducers[CLAIM_SEARCH_BY_TAGS_COMPLETED] = (state, action) => {
-  const fetchingClaimSearchByTags = Object.assign({}, state.fetchingClaimSearchByTags);
-  const claimSearchUrisByTags = Object.assign({}, state.claimSearchUrisByTags);
-  const { append, tags, uris } = action.data;
-
-  if (action.data.append) {
+  if (append) {
     // todo: check for duplicate uris when concatenating?
-    claimSearchUrisByTags[tags] = claimSearchUrisByTags[tags] && claimSearchUrisByTags[tags].length ? claimSearchUrisByTags[tags].concat(uris) : uris;
+    claimSearchByQuery[query] = claimSearchByQuery[query] && claimSearchByQuery[query].length ? claimSearchByQuery[query].concat(uris) : uris;
   } else {
-    claimSearchUrisByTags[tags] = uris;
+    claimSearchByQuery[query] = uris;
   }
-  fetchingClaimSearchByTags[tags] = false; // or delete the key instead?
 
-  return Object.assign({}, state, {
-    claimSearchUrisByTags,
-    fetchingClaimSearchByTags
-  });
+  delete fetchingClaimSearchByQuery[query];
+
+  return Object.assign({}, state, _extends$5({}, handleClaimAction(state, action), {
+    claimSearchByQuery,
+    fetchingClaimSearchByQuery
+  }));
 };
-reducers[CLAIM_SEARCH_BY_TAGS_FAILED] = (state, action) => {
-  const fetchingClaimSearchByTags = Object.assign({}, state.fetchingClaimSearchByTags);
-  fetchingClaimSearchByTags[action.data.tags] = false;
+
+reducers[CLAIM_SEARCH_FAILED] = (state, action) => {
+  const fetchingClaimSearchByQuery = Object.assign({}, state.fetchingClaimSearchByQuery);
+  fetchingClaimSearchByQuery[action.data.tags] = false;
 
   return Object.assign({}, state, {
-    fetchingClaimSearchByTags
+    fetchingClaimSearchByQuery
   });
 };
 
@@ -4784,12 +4710,12 @@ exports.SORT_OPTIONS = sort_options;
 exports.THUMBNAIL_STATUSES = thumbnail_upload_statuses;
 exports.TRANSACTIONS = transaction_types;
 exports.batchActions = batchActions;
-exports.buildClaimSearchCacheQuery = buildClaimSearchCacheQuery;
 exports.buildURI = buildURI;
 exports.claimsReducer = claimsReducer;
 exports.commentReducer = commentReducer;
 exports.contentReducer = contentReducer;
 exports.convertToShareLink = convertToShareLink;
+exports.createNormalizedClaimSearchKey = createNormalizedClaimSearchKey;
 exports.creditsToString = creditsToString;
 exports.doAbandonClaim = doAbandonClaim;
 exports.doAddTag = doAddTag;
@@ -4798,7 +4724,6 @@ exports.doBlurSearchInput = doBlurSearchInput;
 exports.doCheckAddressIsMine = doCheckAddressIsMine;
 exports.doCheckPendingPublishes = doCheckPendingPublishes;
 exports.doClaimSearch = doClaimSearch;
-exports.doClaimSearchByTags = doClaimSearchByTags;
 exports.doClearPublish = doClearPublish;
 exports.doCommentCreate = doCommentCreate;
 exports.doCommentList = doCommentList;
@@ -4908,7 +4833,6 @@ exports.selectBalance = selectBalance;
 exports.selectBlocks = selectBlocks;
 exports.selectChannelClaimCounts = selectChannelClaimCounts;
 exports.selectClaimSearchByQuery = selectClaimSearchByQuery;
-exports.selectClaimSearchUrisByTags = selectClaimSearchUrisByTags;
 exports.selectClaimsById = selectClaimsById;
 exports.selectClaimsByUri = selectClaimsByUri;
 exports.selectCurrentChannelPage = selectCurrentChannelPage;
@@ -4922,7 +4846,6 @@ exports.selectDraftTransactionError = selectDraftTransactionError;
 exports.selectError = selectError;
 exports.selectFailedPurchaseUris = selectFailedPurchaseUris;
 exports.selectFetchingClaimSearch = selectFetchingClaimSearch;
-exports.selectFetchingClaimSearchByTags = selectFetchingClaimSearchByTags;
 exports.selectFetchingMyChannels = selectFetchingMyChannels;
 exports.selectFileInfosByOutpoint = selectFileInfosByOutpoint;
 exports.selectFileInfosDownloaded = selectFileInfosDownloaded;
@@ -4986,6 +4909,7 @@ exports.selectWalletState = selectWalletState;
 exports.selectWalletUnlockPending = selectWalletUnlockPending;
 exports.selectWalletUnlockResult = selectWalletUnlockResult;
 exports.selectWalletUnlockSucceeded = selectWalletUnlockSucceeded;
+exports.selectfetchingClaimSearchByQuery = selectfetchingClaimSearchByQuery;
 exports.setSearchApi = setSearchApi;
 exports.tagsReducer = tagsReducer;
 exports.toQueryString = toQueryString;
diff --git a/src/index.js b/src/index.js
index a8117ec..f6e7d14 100644
--- a/src/index.js
+++ b/src/index.js
@@ -55,7 +55,6 @@ export {
   doCreateChannel,
   doUpdateChannel,
   doClaimSearch,
-  doClaimSearchByTags,
 } from 'redux/actions/claims';
 
 export { doDeletePurchasedUri, doPurchaseUri, doFileGet } from 'redux/actions/file';
@@ -116,8 +115,7 @@ export { doCommentList, doCommentCreate } from 'redux/actions/comments';
 export { batchActions } from 'util/batch-actions';
 export { parseQueryParams, toQueryString } from 'util/query-params';
 export { formatCredits, formatFullPrice, creditsToString } from 'util/format-credits';
-export { isClaimNsfw } from 'util/claim';
-export { buildClaimSearchCacheQuery } from 'util/claim-search';
+export { isClaimNsfw, createNormalizedClaimSearchKey } from 'util/claim';
 
 // reducers
 export { claimsReducer } from 'redux/reducers/claims';
@@ -194,11 +192,10 @@ export {
   selectChannelClaimCounts,
   selectCurrentChannelPage,
   selectFetchingClaimSearch,
-  selectFetchingClaimSearchByTags,
-  selectClaimSearchUrisByTags,
+  selectfetchingClaimSearchByQuery,
+  selectClaimSearchByQuery,
   makeSelectFetchingClaimSearchForTags,
   makeSelectClaimSearchUrisForTags,
-  selectClaimSearchByQuery,
 } from 'redux/selectors/claims';
 
 export { makeSelectCommentsForUri } from 'redux/selectors/comments';
diff --git a/src/redux/actions/claims.js b/src/redux/actions/claims.js
index 20a9a05..902c87b 100644
--- a/src/redux/actions/claims.js
+++ b/src/redux/actions/claims.js
@@ -8,8 +8,7 @@ import { doFetchTransactions } from 'redux/actions/wallet';
 import { selectSupportsByOutpoint } from 'redux/selectors/wallet';
 import { creditsToString } from 'util/format-credits';
 import { batchActions } from 'util/batch-actions';
-import { createNormalizedTagKey } from 'util/claim';
-import { buildClaimSearchCacheQuery } from 'util/claim-search';
+import { createNormalizedClaimSearchKey } from 'util/claim';
 
 export function doResolveUris(uris: Array<string>, returnCachedClaims: boolean = false) {
   return (dispatch: Dispatch, getState: GetState) => {
@@ -315,12 +314,16 @@ export function doFetchChannelListMine() {
   };
 }
 
-export function doClaimSearch(options: { page?: number, release_time?: string } = {}) {
-  const query = buildClaimSearchCacheQuery(options);
-
+export function doClaimSearch(
+  options: { tags?: Array<string>, page?: number, page_size?: number, release_time?: string } = {
+    page_size: 10,
+  }
+) {
+  const query = createNormalizedClaimSearchKey(options);
   return (dispatch: Dispatch) => {
     dispatch({
       type: ACTIONS.CLAIM_SEARCH_STARTED,
+      data: { query: query },
     });
 
     const success = (data: ClaimSearchResponse) => {
@@ -333,62 +336,18 @@ export function doClaimSearch(options: { page?: number, release_time?: string }
 
       dispatch({
         type: ACTIONS.CLAIM_SEARCH_COMPLETED,
-        data: { resolveInfo, uris, query, append: options.page && options.page !== 1 },
+        data: { query, resolveInfo, uris, append: options.page && options.page !== 1 },
       });
     };
 
     const failure = err => {
       dispatch({
         type: ACTIONS.CLAIM_SEARCH_FAILED,
+        data: { query },
         error: err,
       });
     };
 
-    Lbry.claim_search({
-      ...options,
-    }).then(success, failure);
-  };
-}
-
-// tags can be one or many (comma separated)
-export function doClaimSearchByTags(
-  tags: Array<string>,
-  amount: number = 10,
-  options: { page?: number } = {}
-) {
-  return (dispatch: Dispatch) => {
-    const tagList = createNormalizedTagKey(tags);
-    dispatch({
-      type: ACTIONS.CLAIM_SEARCH_BY_TAGS_STARTED,
-      data: { tags: tagList },
-    });
-
-    const success = (data: ClaimSearchResponse) => {
-      const resolveInfo = {};
-      const uris = [];
-      data.items.forEach((stream: Claim) => {
-        resolveInfo[stream.permanent_url] = { stream };
-        uris.push(stream.permanent_url);
-      });
-
-      dispatch({
-        type: ACTIONS.CLAIM_SEARCH_BY_TAGS_COMPLETED,
-        data: { tags: tagList, resolveInfo, uris, append: options.page && options.page !== 1 },
-      });
-    };
-
-    const failure = err => {
-      dispatch({
-        type: ACTIONS.CLAIM_SEARCH_BY_TAGS_FAILED,
-        data: { tags: tagList },
-        error: err,
-      });
-    };
-
-    Lbry.claim_search({
-      page_size: amount,
-      any_tags: tags,
-      ...options,
-    }).then(success, failure);
+    Lbry.claim_search(options).then(success, failure);
   };
 }
diff --git a/src/redux/reducers/claims.js b/src/redux/reducers/claims.js
index 1ef4c40..a001d3a 100644
--- a/src/redux/reducers/claims.js
+++ b/src/redux/reducers/claims.js
@@ -21,9 +21,8 @@ type State = {
   abandoningById: { [string]: boolean },
   fetchingChannelClaims: { [string]: number },
   fetchingMyChannels: boolean,
-  lastClaimSearchUris: Array<string>,
-  fetchingClaimSearchByTags: { [string]: boolean },
-  claimSearchUrisByTags: { [string]: { all: Array<string> } },
+  fetchingClaimSearchByQuery: { [string]: boolean },
+  claimSearchByQuery: { [string]: Array<string> },
   claimsByChannel: {
     [string]: {
       all: Array<string>,
@@ -47,10 +46,8 @@ const defaultState = {
   abandoningById: {},
   pendingById: {},
   claimSearchError: false,
-  fetchingClaimSearch: false,
-  claimSearchUrisByTags: {},
-  fetchingClaimSearchByTags: {},
-  lastClaimSearchUris: [],
+  claimSearchByQuery: {},
+  fetchingClaimSearchByQuery: {},
 };
 
 function handleClaimAction(state: State, action: any): State {
@@ -290,75 +287,45 @@ reducers[ACTIONS.RESOLVE_URIS_STARTED] = (state: State, action: any): State => {
   });
 };
 
-reducers[ACTIONS.CLAIM_SEARCH_STARTED] = (state: State): State => {
+reducers[ACTIONS.CLAIM_SEARCH_STARTED] = (state: State, action: any): State => {
+  const fetchingClaimSearchByQuery = Object.assign({}, state.fetchingClaimSearchByQuery);
+  fetchingClaimSearchByQuery[action.data.query] = true;
+
   return Object.assign({}, state, {
-    fetchingClaimSearch: true,
-    claimSearchError: false,
+    fetchingClaimSearchByQuery,
   });
 };
 
 reducers[ACTIONS.CLAIM_SEARCH_COMPLETED] = (state: State, action: any): State => {
-  const { claimSearchSearchByQuery } = state;
-  const { uris, query, append } = action.data;
+  const fetchingClaimSearchByQuery = Object.assign({}, state.fetchingClaimSearchByQuery);
+  const claimSearchByQuery = Object.assign({}, state.claimSearchByQuery);
+  const { append, query, uris } = action.data;
 
-  let newClaimSearch = { ...claimSearchSearchByQuery };
-  if (!uris) {
-    newClaimSearch[query] = null;
-  } else if (append && newClaimSearch[query]) {
-    newClaimSearch[query] = newClaimSearch[query].concat(uris);
-  } else {
-    newClaimSearch[query] = uris;
-  }
-
-  return {
-    ...handleClaimAction(state, action),
-    fetchingClaimSearch: false,
-    claimSearchSearchByQuery: newClaimSearch,
-  };
-};
-
-reducers[ACTIONS.CLAIM_SEARCH_FAILED] = (state: State): State => {
-  return Object.assign({}, state, {
-    fetchingClaimSearch: false,
-    claimSearchError: true,
-  });
-};
-
-reducers[ACTIONS.CLAIM_SEARCH_BY_TAGS_STARTED] = (state: State, action: any): State => {
-  const fetchingClaimSearchByTags = Object.assign({}, state.fetchingClaimSearchByTags);
-  fetchingClaimSearchByTags[action.data.tags] = true;
-
-  return Object.assign({}, state, {
-    fetchingClaimSearchByTags,
-  });
-};
-reducers[ACTIONS.CLAIM_SEARCH_BY_TAGS_COMPLETED] = (state: State, action: any): State => {
-  const fetchingClaimSearchByTags = Object.assign({}, state.fetchingClaimSearchByTags);
-  const claimSearchUrisByTags = Object.assign({}, state.claimSearchUrisByTags);
-  const { append, tags, uris } = action.data;
-
-  if (action.data.append) {
+  if (append) {
     // todo: check for duplicate uris when concatenating?
-    claimSearchUrisByTags[tags] =
-      claimSearchUrisByTags[tags] && claimSearchUrisByTags[tags].length
-        ? claimSearchUrisByTags[tags].concat(uris)
+    claimSearchByQuery[query] =
+      claimSearchByQuery[query] && claimSearchByQuery[query].length
+        ? claimSearchByQuery[query].concat(uris)
         : uris;
   } else {
-    claimSearchUrisByTags[tags] = uris;
+    claimSearchByQuery[query] = uris;
   }
-  fetchingClaimSearchByTags[tags] = false; // or delete the key instead?
+
+  delete fetchingClaimSearchByQuery[query];
 
   return Object.assign({}, state, {
-    claimSearchUrisByTags,
-    fetchingClaimSearchByTags,
+    ...handleClaimAction(state, action),
+    claimSearchByQuery,
+    fetchingClaimSearchByQuery,
   });
 };
-reducers[ACTIONS.CLAIM_SEARCH_BY_TAGS_FAILED] = (state: State, action: any): State => {
-  const fetchingClaimSearchByTags = Object.assign({}, state.fetchingClaimSearchByTags);
-  fetchingClaimSearchByTags[action.data.tags] = false;
+
+reducers[ACTIONS.CLAIM_SEARCH_FAILED] = (state: State, action: any): State => {
+  const fetchingClaimSearchByQuery = Object.assign({}, state.fetchingClaimSearchByQuery);
+  fetchingClaimSearchByQuery[action.data.tags] = false;
 
   return Object.assign({}, state, {
-    fetchingClaimSearchByTags,
+    fetchingClaimSearchByQuery,
   });
 };
 
diff --git a/src/redux/selectors/claims.js b/src/redux/selectors/claims.js
index fee7b2a..85ff987 100644
--- a/src/redux/selectors/claims.js
+++ b/src/redux/selectors/claims.js
@@ -2,7 +2,7 @@
 import { normalizeURI, buildURI, parseURI } from 'lbryURI';
 import { selectSearchUrisByQuery } from 'redux/selectors/search';
 import { createSelector } from 'reselect';
-import { isClaimNsfw, createNormalizedTagKey } from 'util/claim';
+import { isClaimNsfw, createNormalizedClaimSearchKey } from 'util/claim';
 import { getSearchQueryString } from 'util/query-params';
 
 const selectState = state => state.claims || {};
@@ -223,8 +223,8 @@ export const makeSelectDateForUri = (uri: string) =>
         (claim.value.release_time
           ? claim.value.release_time * 1000
           : claim.meta.creation_timestamp
-          ? claim.meta.creation_timestamp * 1000
-          : null);
+            ? claim.meta.creation_timestamp * 1000
+            : null);
       if (!timestamp) {
         return undefined;
       }
@@ -498,40 +498,34 @@ export const makeSelectTagsForUri = (uri: string) =>
     }
   );
 
-export const selectFetchingClaimSearch = createSelector(
+export const selectfetchingClaimSearchByQuery = createSelector(
   selectState,
-  state => state.fetchingClaimSearch
+  state => state.fetchingClaimSearchByQuery || {}
+);
+
+export const selectFetchingClaimSearch = createSelector(
+  selectfetchingClaimSearchByQuery,
+  fetchingClaimSearchByQuery => Boolean(Object.keys(fetchingClaimSearchByQuery).length)
 );
 
 export const selectClaimSearchByQuery = createSelector(
   selectState,
-  state => state.claimSearchSearchByQuery || {}
+  state => state.claimSearchByQuery || {}
 );
 
+export const makeSelectClaimSearchUrisByOptions = (options: {}) =>
+  createSelector(
+    selectClaimSearchByQuery,
+    byQuery => {
+      // We don't care what options are passed to this selector. Just forward them.
+      // $FlowFixMe
+      const query = createNormalizedClaimSearchKey(options);
+      return byQuery[query];
+    }
+  );
+
 export const makeSelectShortUrlForUri = (uri: string) =>
   createSelector(
     makeSelectClaimForUri(uri),
     claim => claim && claim.short_url
   );
-
-export const selectFetchingClaimSearchByTags = createSelector(
-  selectState,
-  state => state.fetchingClaimSearchByTags
-);
-
-export const selectClaimSearchUrisByTags = createSelector(
-  selectState,
-  state => state.claimSearchUrisByTags
-);
-
-export const makeSelectFetchingClaimSearchForTags = (tags: Array<string>) =>
-  createSelector(
-    selectFetchingClaimSearchByTags,
-    byTags => byTags[createNormalizedTagKey(tags)]
-  );
-
-export const makeSelectClaimSearchUrisForTags = (tags: Array<string>) =>
-  createSelector(
-    selectClaimSearchUrisByTags,
-    byTags => byTags[createNormalizedTagKey(tags)]
-  );
diff --git a/src/util/claim-search.js b/src/util/claim-search.js
deleted file mode 100644
index 44913cf..0000000
--- a/src/util/claim-search.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// @flow
-export function buildClaimSearchCacheQuery(options: { page?: number, release_time?: string }) {
-  // Ignore page because we don't care what the last page searched was, we want everything
-  // Ignore release_time because that will change depending on when you call claim_search ex: release_time: ">12344567"
-  const { page: optionToIgnoreForQuery, release_time: anotherToIgnore, ...rest } = options;
-  const query = JSON.stringify(rest);
-  return query;
-}
diff --git a/src/util/claim.js b/src/util/claim.js
index 1e24a10..ece6fc7 100644
--- a/src/util/claim.js
+++ b/src/util/claim.js
@@ -23,6 +23,10 @@ export const isClaimNsfw = (claim: Claim): boolean => {
   return false;
 };
 
-export const createNormalizedTagKey = (tags: Array<string>): string => {
-  return tags ? tags.sort().join(',') : '';
-};
+export function createNormalizedClaimSearchKey(options: { page?: number, release_time?: string }) {
+  // Ignore page because we don't care what the last page searched was, we want everything
+  // Ignore release_time because that will change depending on when you call claim_search ex: release_time: ">12344567"
+  const { page: optionToIgnoreForQuery, release_time: anotherToIgnore, ...rest } = options;
+  const query = JSON.stringify(rest);
+  return query;
+}

From c04f6806f7fb3eb77a19ede8651d1498eccfe16c Mon Sep 17 00:00:00 2001
From: Sean Yesmunt <sean@lbry.io>
Date: Tue, 30 Jul 2019 12:00:36 -0400
Subject: [PATCH 5/8] fix export and url selector

---
 dist/bundle.es.js             | 12 +++++-------
 src/index.js                  |  2 --
 src/redux/selectors/claims.js |  8 +++++---
 3 files changed, 10 insertions(+), 12 deletions(-)

diff --git a/dist/bundle.es.js b/dist/bundle.es.js
index e0a8de9..3311298 100644
--- a/dist/bundle.es.js
+++ b/dist/bundle.es.js
@@ -1408,7 +1408,11 @@ const makeSelectContentTypeForUri = uri => reselect.createSelector(makeSelectCla
 
 const makeSelectThumbnailForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
   const thumbnail = claim && claim.value && claim.value.thumbnail;
-  return thumbnail && thumbnail.url && thumbnail.url.trim().length > 0 ? thumbnail.url : undefined;
+  if (!thumbnail || !thumbnail.url) {
+    return undefined;
+  }
+
+  return thumbnail.url.trim();
 });
 
 const makeSelectCoverForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
@@ -1565,10 +1569,6 @@ const selectClaimSearchByQuery = reselect.createSelector(selectState$1, state =>
 
 const makeSelectShortUrlForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => claim && claim.short_url);
 
-const makeSelectFetchingClaimSearchForTags = tags => reselect.createSelector(selectfetchingClaimSearchByQuery, byQuery => byQuery[createNormalizedClaimSearchKey({ any_tags: tags })]);
-
-const makeSelectClaimSearchUrisForTags = tags => reselect.createSelector(selectClaimSearchByQuery, byQuery => byQuery[createNormalizedClaimSearchKey({ any_tags: tags })]);
-
 const selectState$2 = state => state.wallet || {};
 
 const selectWalletState = selectState$2;
@@ -4785,7 +4785,6 @@ exports.makeSelectClaimForUri = makeSelectClaimForUri;
 exports.makeSelectClaimIsMine = makeSelectClaimIsMine;
 exports.makeSelectClaimIsNsfw = makeSelectClaimIsNsfw;
 exports.makeSelectClaimIsPending = makeSelectClaimIsPending;
-exports.makeSelectClaimSearchUrisForTags = makeSelectClaimSearchUrisForTags;
 exports.makeSelectClaimsInChannelForCurrentPageState = makeSelectClaimsInChannelForCurrentPageState;
 exports.makeSelectClaimsInChannelForPage = makeSelectClaimsInChannelForPage;
 exports.makeSelectCommentsForUri = makeSelectCommentsForUri;
@@ -4795,7 +4794,6 @@ exports.makeSelectCoverForUri = makeSelectCoverForUri;
 exports.makeSelectDateForUri = makeSelectDateForUri;
 exports.makeSelectDownloadingForUri = makeSelectDownloadingForUri;
 exports.makeSelectFetchingChannelClaims = makeSelectFetchingChannelClaims;
-exports.makeSelectFetchingClaimSearchForTags = makeSelectFetchingClaimSearchForTags;
 exports.makeSelectFileInfoForUri = makeSelectFileInfoForUri;
 exports.makeSelectFirstRecommendedFileForUri = makeSelectFirstRecommendedFileForUri;
 exports.makeSelectIsUriResolving = makeSelectIsUriResolving;
diff --git a/src/index.js b/src/index.js
index f6e7d14..05690a8 100644
--- a/src/index.js
+++ b/src/index.js
@@ -194,8 +194,6 @@ export {
   selectFetchingClaimSearch,
   selectfetchingClaimSearchByQuery,
   selectClaimSearchByQuery,
-  makeSelectFetchingClaimSearchForTags,
-  makeSelectClaimSearchUrisForTags,
 } from 'redux/selectors/claims';
 
 export { makeSelectCommentsForUri } from 'redux/selectors/comments';
diff --git a/src/redux/selectors/claims.js b/src/redux/selectors/claims.js
index 85ff987..180f84c 100644
--- a/src/redux/selectors/claims.js
+++ b/src/redux/selectors/claims.js
@@ -255,9 +255,11 @@ export const makeSelectThumbnailForUri = (uri: string) =>
     makeSelectClaimForUri(uri),
     claim => {
       const thumbnail = claim && claim.value && claim.value.thumbnail;
-      return thumbnail && thumbnail.url && thumbnail.url.trim().length > 0
-        ? thumbnail.url
-        : undefined;
+      if (!thumbnail || !thumbnail.url) {
+        return undefined;
+      }
+
+      return thumbnail.url.trim();
     }
   );
 

From 3266ebd933bc4af5e24c62c3ca956b5ee967db32 Mon Sep 17 00:00:00 2001
From: Sean Yesmunt <sean@lbry.io>
Date: Tue, 30 Jul 2019 12:04:04 -0400
Subject: [PATCH 6/8] delete unused navigation file

---
 src/redux/selectors/navigation.js | 78 -------------------------------
 1 file changed, 78 deletions(-)
 delete mode 100644 src/redux/selectors/navigation.js

diff --git a/src/redux/selectors/navigation.js b/src/redux/selectors/navigation.js
deleted file mode 100644
index 3b81d79..0000000
--- a/src/redux/selectors/navigation.js
+++ /dev/null
@@ -1,78 +0,0 @@
-import { createSelector } from 'reselect';
-import { parseQueryParams } from 'util/query-params';
-
-export const selectState = state => state.navigation || {};
-
-export const selectCurrentPath = createSelector(
-  selectState,
-  state => state.currentPath
-);
-
-export const computePageFromPath = path => (path ? path.replace(/^\//, '').split('?')[0] : '');
-
-export const selectCurrentPage = createSelector(
-  selectCurrentPath,
-  path => computePageFromPath(path)
-);
-
-export const selectCurrentParams = createSelector(
-  selectCurrentPath,
-  path => {
-    if (path === undefined) return {};
-    if (!path.match(/\?/)) return {};
-
-    return parseQueryParams(path.split('?')[1]);
-  }
-);
-
-export const makeSelectCurrentParam = param =>
-  createSelector(
-    selectCurrentParams,
-    params => (params ? params[param] : undefined)
-  );
-
-export const selectPathAfterAuth = createSelector(
-  selectState,
-  state => state.pathAfterAuth
-);
-
-export const selectIsBackDisabled = createSelector(
-  selectState,
-  state => state.index === 0
-);
-
-export const selectIsForwardDisabled = createSelector(
-  selectState,
-  state => state.index === state.stack.length - 1
-);
-
-export const selectIsHome = createSelector(
-  selectCurrentPage,
-  page => page === 'discover'
-);
-
-export const selectHistoryIndex = createSelector(
-  selectState,
-  state => state.index
-);
-
-export const selectHistoryStack = createSelector(
-  selectState,
-  state => state.stack
-);
-
-// returns current page attributes (scrollY, path)
-export const selectActiveHistoryEntry = createSelector(
-  selectState,
-  state => state.stack[state.index]
-);
-
-export const selectPageTitle = createSelector(
-  selectCurrentPage,
-  page => {
-    switch (page) {
-      default:
-        return '';
-    }
-  }
-);

From b257431eae9ff2e3bdb1db7fd7cee389cd8b7fd9 Mon Sep 17 00:00:00 2001
From: Sean Yesmunt <sean@lbry.io>
Date: Tue, 30 Jul 2019 12:15:29 -0400
Subject: [PATCH 7/8] remove /g flag for uri regex

---
 dist/bundle.es.js | 2 +-
 src/lbryURI.js    | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/dist/bundle.es.js b/dist/bundle.es.js
index 3311298..984aefe 100644
--- a/dist/bundle.es.js
+++ b/dist/bundle.es.js
@@ -902,7 +902,7 @@ const channelNameMinLength = 1;
 const claimIdMaxLength = 40;
 
 // see https://spec.lbry.com/#urls
-const regexInvalidURI = /[ =&#:$@%?;/\\"<>%{}|^~[\]`\u{0000}-\u{0008}\u{000b}-\u{000c}\u{000e}-\u{001F}\u{D800}-\u{DFFF}\u{FFFE}-\u{FFFF}]/gu;
+const regexInvalidURI = /[ =&#:$@%?;/\\"<>%{}|^~[\]`\u{0000}-\u{0008}\u{000b}-\u{000c}\u{000e}-\u{001F}\u{D800}-\u{DFFF}\u{FFFE}-\u{FFFF}]/u;
 const regexAddress = /^(b|r)(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
 
 /**
diff --git a/src/lbryURI.js b/src/lbryURI.js
index 32c6ce6..72a6200 100644
--- a/src/lbryURI.js
+++ b/src/lbryURI.js
@@ -2,7 +2,7 @@ const channelNameMinLength = 1;
 const claimIdMaxLength = 40;
 
 // see https://spec.lbry.com/#urls
-export const regexInvalidURI = /[ =&#:$@%?;/\\"<>%{}|^~[\]`\u{0000}-\u{0008}\u{000b}-\u{000c}\u{000e}-\u{001F}\u{D800}-\u{DFFF}\u{FFFE}-\u{FFFF}]/gu;
+export const regexInvalidURI = /[ =&#:$@%?;/\\"<>%{}|^~[\]`\u{0000}-\u{0008}\u{000b}-\u{000c}\u{000e}-\u{001F}\u{D800}-\u{DFFF}\u{FFFE}-\u{FFFF}]/u;
 export const regexAddress = /^(b|r)(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
 
 /**

From 2c70556bdfcc4abfffc1a4df9fa410d34638915d Mon Sep 17 00:00:00 2001
From: Sean Yesmunt <sean@lbry.io>
Date: Wed, 31 Jul 2019 15:03:56 -0400
Subject: [PATCH 8/8] use null over undefined

---
 dist/bundle.es.js             | 2 +-
 src/redux/selectors/claims.js | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/dist/bundle.es.js b/dist/bundle.es.js
index 984aefe..e80bf8e 100644
--- a/dist/bundle.es.js
+++ b/dist/bundle.es.js
@@ -1409,7 +1409,7 @@ const makeSelectContentTypeForUri = uri => reselect.createSelector(makeSelectCla
 const makeSelectThumbnailForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => {
   const thumbnail = claim && claim.value && claim.value.thumbnail;
   if (!thumbnail || !thumbnail.url) {
-    return undefined;
+    return null;
   }
 
   return thumbnail.url.trim();
diff --git a/src/redux/selectors/claims.js b/src/redux/selectors/claims.js
index 180f84c..c9dc1ac 100644
--- a/src/redux/selectors/claims.js
+++ b/src/redux/selectors/claims.js
@@ -256,7 +256,7 @@ export const makeSelectThumbnailForUri = (uri: string) =>
     claim => {
       const thumbnail = claim && claim.value && claim.value.thumbnail;
       if (!thumbnail || !thumbnail.url) {
-        return undefined;
+        return null;
       }
 
       return thumbnail.url.trim();