diff --git a/package.json b/package.json index d1b3ee739..505fd3041 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "match-sorter": "^6.3.0", "parse-duration": "^1.0.0", "proxy-polyfill": "0.1.6", + "re-reselect": "^4.0.0", "react-datetime-picker": "^3.2.1", "react-plastic": "^1.1.1", "react-top-loading-bar": "^2.0.1", diff --git a/ui/component/claimPreview/index.js b/ui/component/claimPreview/index.js index b63696f69..25ab46a8b 100644 --- a/ui/component/claimPreview/index.js +++ b/ui/component/claimPreview/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { - makeSelectClaimForUri, + selectClaimForUri, makeSelectIsUriResolving, makeSelectClaimIsMine, makeSelectClaimIsPending, @@ -8,7 +8,7 @@ import { makeSelectReflectingClaimForUri, makeSelectClaimWasPurchased, makeSelectTitleForUri, - makeSelectDateForUri, + selectDateForUri, } from 'redux/selectors/claims'; import { makeSelectStreamingUrlForUri } from 'redux/selectors/file_info'; import { @@ -30,14 +30,14 @@ import ClaimPreview from './view'; import formatMediaDuration from 'util/formatMediaDuration'; const select = (state, props) => { - const claim = props.uri && makeSelectClaimForUri(props.uri)(state); + const claim = props.uri && selectClaimForUri(state, props.uri); const media = claim && claim.value && (claim.value.video || claim.value.audio); const mediaDuration = media && media.duration && formatMediaDuration(media.duration, { screenReader: true }); return { claim, mediaDuration, - date: props.uri && makeSelectDateForUri(props.uri)(state), + date: props.uri && selectDateForUri(state, props.uri), title: props.uri && makeSelectTitleForUri(props.uri)(state), pending: props.uri && makeSelectClaimIsPending(props.uri)(state), reflectingProgress: props.uri && makeSelectReflectingClaimForUri(props.uri)(state), @@ -45,7 +45,6 @@ const select = (state, props) => { claimIsMine: props.uri && makeSelectClaimIsMine(props.uri)(state), isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state), isResolvingRepost: props.uri && makeSelectIsUriResolving(props.repostUrl)(state), - repostClaim: props.uri && makeSelectClaimForUri(props.uri)(state), nsfw: props.uri && makeSelectClaimIsNsfw(props.uri)(state), blackListedOutpoints: selectBlackListedOutpoints(state), filteredOutpoints: selectFilteredOutpoints(state), diff --git a/ui/component/claimPreviewTile/index.js b/ui/component/claimPreviewTile/index.js index b3be8f36d..eaf64a865 100644 --- a/ui/component/claimPreviewTile/index.js +++ b/ui/component/claimPreviewTile/index.js @@ -6,7 +6,7 @@ import { makeSelectTitleForUri, makeSelectChannelForClaimUri, makeSelectClaimIsNsfw, - makeSelectDateForUri, + selectDateForUri, } from 'redux/selectors/claims'; import { doFileGet } from 'redux/actions/file'; import { doResolveUri } from 'redux/actions/claims'; @@ -24,7 +24,7 @@ const select = (state, props) => { return { claim, mediaDuration, - date: props.uri && makeSelectDateForUri(props.uri)(state), + date: props.uri && selectDateForUri(state, props.uri), channel: props.uri && makeSelectChannelForClaimUri(props.uri)(state), isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state), thumbnail: props.uri && makeSelectThumbnailForUri(props.uri)(state), diff --git a/ui/component/dateTime/index.js b/ui/component/dateTime/index.js index 367ac4ab1..24fc98280 100644 --- a/ui/component/dateTime/index.js +++ b/ui/component/dateTime/index.js @@ -1,11 +1,11 @@ import { connect } from 'react-redux'; -import { makeSelectDateForUri } from 'redux/selectors/claims'; +import { selectDateForUri } from 'redux/selectors/claims'; import * as SETTINGS from 'constants/settings'; import { makeSelectClientSetting } from 'redux/selectors/settings'; import DateTime from './view'; const select = (state, props) => ({ - date: props.date || makeSelectDateForUri(props.uri)(state), + date: props.date || selectDateForUri(state, props.uri), clock24h: makeSelectClientSetting(SETTINGS.CLOCK_24H)(state), }); export default connect(select)(DateTime); diff --git a/ui/redux/selectors/claims.js b/ui/redux/selectors/claims.js index 6cb544820..06f26fd1f 100644 --- a/ui/redux/selectors/claims.js +++ b/ui/redux/selectors/claims.js @@ -2,20 +2,22 @@ import { normalizeURI, parseURI, isURIValid } from 'util/lbryURI'; import { selectSupportsByOutpoint } from 'redux/selectors/wallet'; import { createSelector } from 'reselect'; +import { createCachedSelector } from 're-reselect'; import { isClaimNsfw, filterClaims } from 'util/claim'; import * as CLAIM from 'constants/claim'; +type State = { claims: any }; + const selectState = (state) => state.claims || {}; -export const selectById = createSelector(selectState, (state) => state.byId || {}); - -export const selectPendingClaimsById = createSelector(selectState, (state) => state.pendingById || {}); +export const selectById = (state: State) => selectState(state).byId || {}; +export const selectPendingClaimsById = (state: State) => selectState(state).pendingById || {}; export const selectClaimsById = createSelector(selectById, selectPendingClaimsById, (byId, pendingById) => { return Object.assign(byId, pendingById); // do I need merged to keep metadata? }); -export const selectClaimIdsByUri = createSelector(selectState, (state) => state.claimsByUri || {}); +export const selectClaimIdsByUri = (state: State) => selectState(state).claimsByUri || {}; export const selectCurrentChannelPage = createSelector(selectState, (state) => state.currentChannelPage || 1); @@ -74,6 +76,43 @@ export const selectReflectingById = createSelector(selectState, (state) => state export const makeSelectClaimForClaimId = (claimId: string) => createSelector(selectClaimsById, (byId) => byId[claimId]); +export const selectClaimForUri = createCachedSelector( + selectClaimIdsByUri, + selectClaimsById, + (state, uri) => uri, + (state, uri, returnRepost = true) => returnRepost, + (byUri, byId, uri, returnRepost) => { + const validUri = isURIValid(uri); + + if (validUri && byUri) { + const claimId = uri && byUri[normalizeURI(uri)]; + const claim = byId[claimId]; + + // Make sure to return the claim as is so apps can check if it's been resolved before (null) or still needs to be resolved (undefined) + if (claimId === null) { + return null; + } else if (claimId === undefined) { + return undefined; + } + + const repostedClaim = claim && claim.reposted_claim; + if (repostedClaim && returnRepost) { + const channelUrl = + claim.signing_channel && (claim.signing_channel.canonical_url || claim.signing_channel.permanent_url); + + return { + ...repostedClaim, + repost_url: normalizeURI(uri), + repost_channel_url: channelUrl, + repost_bid_amount: claim && claim.meta && claim.meta.effective_amount, + }; + } else { + return claim; + } + } + } +)((state, uri, returnRepost = true) => `${uri}:${returnRepost ? '1' : '0'}`); + export const makeSelectClaimForUri = (uri: string, returnRepost: boolean = true) => createSelector(selectClaimIdsByUri, selectClaimsById, (byUri, byId) => { const validUri = isURIValid(uri); @@ -242,8 +281,9 @@ export const makeSelectMetadataItemForUri = (uri: string, key: string) => export const makeSelectTitleForUri = (uri: string) => createSelector(makeSelectMetadataForUri(uri), (metadata) => metadata && metadata.title); -export const makeSelectDateForUri = (uri: string) => - createSelector(makeSelectClaimForUri(uri), (claim) => { +export const selectDateForUri = createCachedSelector( + selectClaimForUri, // input: (state, uri, ?returnRepost) + (claim) => { const timestamp = claim && claim.value && @@ -257,7 +297,8 @@ export const makeSelectDateForUri = (uri: string) => } const dateObj = new Date(timestamp); return dateObj; - }); + } +)((state, uri) => uri); export const makeSelectAmountForUri = (uri: string) => createSelector(makeSelectClaimForUri(uri), (claim) => { diff --git a/yarn.lock b/yarn.lock index 66c53b697..571bdedf8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13179,6 +13179,11 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" +re-reselect@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/re-reselect/-/re-reselect-4.0.0.tgz#9ddec4c72c4d952f68caa5aa4b76a9ed38b75cac" + integrity sha512-wuygyq8TXUlSdVXv2kigXxQNOgdb9m7LbIjwfTNGSpaY1riLd5e+VeQjlQMyUtrk0oiyhi1AqIVynworl3qxHA== + react-async-script@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/react-async-script/-/react-async-script-1.1.1.tgz#f481c6c5f094bf4b94a9d52da0d0dda2e1a74bdf"