diff --git a/dist/bundle.es.js b/dist/bundle.es.js index f7b480f..e80bf8e 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}$/; /** @@ -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; +} // @@ -1308,12 +1314,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) { @@ -1400,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 null; + } + + return thumbnail.url.trim(); }); const makeSelectCoverForUri = uri => reselect.createSelector(makeSelectClaimForUri(uri), claim => { @@ -1549,20 +1561,14 @@ 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 selectLastClaimSearchUris = reselect.createSelector(selectState$1, state => state.lastClaimSearchUris); +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 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 selectState$2 = state => state.wallet || {}; const selectWalletState = selectState$2; @@ -2084,6 +2090,14 @@ function doUpdateBlockHeight() { }); } +// https://github.com/reactjs/redux/issues/911 +function batchActions(...actions) { + return { + type: 'BATCH_ACTIONS', + actions + }; +} + 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) { @@ -2366,10 +2380,14 @@ function doFetchChannelListMine() { }; } -function doClaimSearch(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 => { @@ -2382,56 +2400,19 @@ function doClaimSearch(options = {}) { dispatch({ type: CLAIM_SEARCH_COMPLETED, - data: { resolveInfo, uris, 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); }; } @@ -2780,20 +2761,12 @@ function doSetFileListSort(page, value) { }; } -// 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; } +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,10 +3448,9 @@ const defaultState = { fetchingMyChannels: false, abandoningById: {}, pendingById: {}, - fetchingClaimSearch: false, - claimSearchUrisByTags: {}, - fetchingClaimSearchByTags: {}, - lastClaimSearchUris: [] + claimSearchError: false, + claimSearchByQuery: {}, + fetchingClaimSearchByQuery: {} }; function handleClaimAction(state, action) { @@ -3707,64 +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 + fetchingClaimSearchByQuery }); }; + reducers[CLAIM_SEARCH_COMPLETED] = (state, action) => { - const { lastClaimSearchUris } = state; + const fetchingClaimSearchByQuery = Object.assign({}, state.fetchingClaimSearchByQuery); + const claimSearchByQuery = Object.assign({}, state.claimSearchByQuery); + const { append, query, uris } = action.data; - let newClaimSearchUris = []; - if (action.data.append) { - newClaimSearchUris = lastClaimSearchUris.concat(action.data.uris); - } else { - newClaimSearchUris = action.data.uris; - } - - return _extends$5({}, handleClaimAction(state, action), { - fetchingClaimSearch: false, - lastClaimSearchUris: newClaimSearchUris - }); -}; -reducers[CLAIM_SEARCH_FAILED] = state => { - return Object.assign({}, state, { - fetchingClaimSearch: false - }); -}; - -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 }); }; @@ -4209,7 +4158,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 +4208,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 @@ -4766,6 +4715,7 @@ exports.claimsReducer = claimsReducer; exports.commentReducer = commentReducer; exports.contentReducer = contentReducer; exports.convertToShareLink = convertToShareLink; +exports.createNormalizedClaimSearchKey = createNormalizedClaimSearchKey; exports.creditsToString = creditsToString; exports.doAbandonClaim = doAbandonClaim; exports.doAddTag = doAddTag; @@ -4774,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; @@ -4836,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; @@ -4846,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; @@ -4883,7 +4830,7 @@ exports.selectAllMyClaimsByOutpoint = selectAllMyClaimsByOutpoint; exports.selectBalance = selectBalance; exports.selectBlocks = selectBlocks; exports.selectChannelClaimCounts = selectChannelClaimCounts; -exports.selectClaimSearchUrisByTags = selectClaimSearchUrisByTags; +exports.selectClaimSearchByQuery = selectClaimSearchByQuery; exports.selectClaimsById = selectClaimsById; exports.selectClaimsByUri = selectClaimsByUri; exports.selectCurrentChannelPage = selectCurrentChannelPage; @@ -4897,7 +4844,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; @@ -4914,7 +4860,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; @@ -4962,6 +4907,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 07a30dd..05690a8 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'; @@ -113,10 +112,10 @@ 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 { isClaimNsfw } from 'util/claim'; +export { batchActions } from 'util/batch-actions'; +export { parseQueryParams, toQueryString } from 'util/query-params'; +export { formatCredits, formatFullPrice, creditsToString } from 'util/format-credits'; +export { isClaimNsfw, createNormalizedClaimSearchKey } from 'util/claim'; // reducers export { claimsReducer } from 'redux/reducers/claims'; @@ -193,11 +192,8 @@ export { selectChannelClaimCounts, selectCurrentChannelPage, selectFetchingClaimSearch, - selectLastClaimSearchUris, - selectFetchingClaimSearchByTags, - selectClaimSearchUrisByTags, - makeSelectFetchingClaimSearchForTags, - makeSelectClaimSearchUrisForTags, + selectfetchingClaimSearchByQuery, + selectClaimSearchByQuery, } from 'redux/selectors/claims'; export { makeSelectCommentsForUri } from 'redux/selectors/comments'; diff --git a/src/lbryURI.js b/src/lbryURI.js index 42045d0..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}$/; /** diff --git a/src/redux/actions/claims.js b/src/redux/actions/claims.js index 34e1be3..902c87b 100644 --- a/src/redux/actions/claims.js +++ b/src/redux/actions/claims.js @@ -6,9 +6,9 @@ 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 { createNormalizedTagKey } from 'util/claim'; +import { creditsToString } from 'util/format-credits'; +import { batchActions } from 'util/batch-actions'; +import { createNormalizedClaimSearchKey } from 'util/claim'; export function doResolveUris(uris: Array<string>, returnCachedClaims: boolean = false) { return (dispatch: Dispatch, getState: GetState) => { @@ -314,10 +314,16 @@ export function doFetchChannelListMine() { }; } -export function doClaimSearch(options: { page_size?: number, page?: number } = {}) { +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) => { @@ -330,62 +336,18 @@ 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: { 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/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..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() { @@ -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..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>, @@ -46,10 +45,9 @@ const defaultState = { fetchingMyChannels: false, abandoningById: {}, pendingById: {}, - fetchingClaimSearch: false, - claimSearchUrisByTags: {}, - fetchingClaimSearchByTags: {}, - lastClaimSearchUris: [], + claimSearchError: false, + claimSearchByQuery: {}, + fetchingClaimSearchByQuery: {}, }; function handleClaimAction(state: State, action: any): State { @@ -289,66 +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, + fetchingClaimSearchByQuery, }); }; + reducers[ACTIONS.CLAIM_SEARCH_COMPLETED] = (state: State, action: any): State => { - const { lastClaimSearchUris } = state; + const fetchingClaimSearchByQuery = Object.assign({}, state.fetchingClaimSearchByQuery); + const claimSearchByQuery = Object.assign({}, state.claimSearchByQuery); + const { append, query, uris } = action.data; - let newClaimSearchUris = []; - if (action.data.append) { - newClaimSearchUris = lastClaimSearchUris.concat(action.data.uris); - } else { - newClaimSearchUris = action.data.uris; - } - - return { - ...handleClaimAction(state, action), - fetchingClaimSearch: false, - lastClaimSearchUris: newClaimSearchUris, - }; -}; -reducers[ACTIONS.CLAIM_SEARCH_FAILED] = (state: State): State => { - return Object.assign({}, state, { - fetchingClaimSearch: false, - }); -}; - -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) : 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 2019e18..c9dc1ac 100644 --- a/src/redux/selectors/claims.js +++ b/src/redux/selectors/claims.js @@ -2,8 +2,8 @@ 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 { isClaimNsfw, createNormalizedClaimSearchKey } from 'util/claim'; +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) { @@ -221,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; } @@ -253,7 +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 null; + } + + return thumbnail.url.trim(); } ); @@ -494,40 +500,34 @@ export const makeSelectTagsForUri = (uri: string) => } ); -export const selectFetchingClaimSearch = createSelector( +export const selectfetchingClaimSearchByQuery = createSelector( selectState, - state => state.fetchingClaimSearch + state => state.fetchingClaimSearchByQuery || {} ); -export const selectLastClaimSearchUris = createSelector( - selectState, - state => state.lastClaimSearchUris +export const selectFetchingClaimSearch = createSelector( + selectfetchingClaimSearchByQuery, + fetchingClaimSearchByQuery => Boolean(Object.keys(fetchingClaimSearchByQuery).length) ); +export const selectClaimSearchByQuery = createSelector( + selectState, + 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/redux/selectors/navigation.js b/src/redux/selectors/navigation.js deleted file mode 100644 index 8aafd3c..0000000 --- a/src/redux/selectors/navigation.js +++ /dev/null @@ -1,50 +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 ''; - } -}); 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.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; +} 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