From 093c427b83876c7125788ec36b1f36f7cf3b9854 Mon Sep 17 00:00:00 2001 From: infinite-persistence Date: Thu, 9 Sep 2021 15:02:31 +0800 Subject: [PATCH] Show content view counts on channel pages ## Issue 3587 Show content view counts on channel pages ## Notes Limited to just "channel pages" for now as specified in the ticket. Can be enabled for all claim previews, as long as there's an efficient spot to run the batch fetching. Either make `fetchViewCount` prop default to true, or add the parameter in places that need it. --- package.json | 2 +- ui/component/channelContent/view.jsx | 1 + ui/component/claimListDiscover/index.js | 4 +++ ui/component/claimListDiscover/view.jsx | 22 ++++++++++++++++ ui/component/claimPreviewSubtitle/view.jsx | 10 +++++++- ui/component/claimPreviewTile/view.jsx | 15 +++++------ ui/component/claimTilesDiscover/index.js | 2 ++ ui/component/claimTilesDiscover/view.jsx | 21 ++++++++++++++- ui/component/fileViewCountInline/index.js | 13 ++++++++++ ui/component/fileViewCountInline/view.jsx | 30 ++++++++++++++++++++++ ui/scss/component/_claim-list.scss | 17 +++++++++--- yarn.lock | 4 +-- 12 files changed, 125 insertions(+), 16 deletions(-) create mode 100644 ui/component/fileViewCountInline/index.js create mode 100644 ui/component/fileViewCountInline/view.jsx diff --git a/package.json b/package.json index 2d7ba6a2a..f748de73d 100644 --- a/package.json +++ b/package.json @@ -155,7 +155,7 @@ "json-loader": "^0.5.4", "lbry-format": "https://github.com/lbryio/lbry-format.git", "lbry-redux": "lbryio/lbry-redux#49b9db5aae8db84ec21231444bf8345d450812b2", - "lbryinc": "lbryio/lbryinc#8f9a58bfc8312a65614fd7327661cdcc502c4e59", + "lbryinc": "lbryio/lbryinc#0b4e41ef90d6347819dd3453f2f9398a5c1b4f36", "lint-staged": "^7.0.2", "localforage": "^1.7.1", "lodash-es": "^4.17.14", diff --git a/ui/component/channelContent/view.jsx b/ui/component/channelContent/view.jsx index 917568b8c..a6981cd15 100644 --- a/ui/component/channelContent/view.jsx +++ b/ui/component/channelContent/view.jsx @@ -146,6 +146,7 @@ function ChannelContent(props: Props) { defaultFreshness={CS.FRESH_ALL} showHiddenByUser={viewHiddenChannels} forceShowReposts + fetchViewCount hideFilters={!showFilters} hideAdvancedFilter={!showFilters} tileLayout={tileLayout} diff --git a/ui/component/claimListDiscover/index.js b/ui/component/claimListDiscover/index.js index db5d76259..f1d93ca49 100644 --- a/ui/component/claimListDiscover/index.js +++ b/ui/component/claimListDiscover/index.js @@ -1,6 +1,7 @@ import { connect } from 'react-redux'; import { doClaimSearch, + selectClaimsByUri, selectClaimSearchByQuery, selectClaimSearchByQueryLastPageReached, selectFetchingClaimSearch, @@ -12,11 +13,13 @@ import { doToggleTagFollowDesktop } from 'redux/actions/tags'; import { makeSelectClientSetting, selectShowMatureContent, selectLanguage } from 'redux/selectors/settings'; import { selectModerationBlockList } from 'redux/selectors/comments'; import ClaimListDiscover from './view'; +import { doFetchViewCount } from 'lbryinc'; const select = (state) => ({ followedTags: selectFollowedTags(state), claimSearchByQuery: selectClaimSearchByQuery(state), claimSearchByQueryLastPageReached: selectClaimSearchByQueryLastPageReached(state), + claimsByUri: selectClaimsByUri(state), loading: selectFetchingClaimSearch(state), showNsfw: selectShowMatureContent(state), hideReposts: makeSelectClientSetting(SETTINGS.HIDE_REPOSTS)(state), @@ -29,6 +32,7 @@ const select = (state) => ({ const perform = { doClaimSearch, doToggleTagFollowDesktop, + doFetchViewCount, }; export default connect(select, perform)(ClaimListDiscover); diff --git a/ui/component/claimListDiscover/view.jsx b/ui/component/claimListDiscover/view.jsx index 843541054..5fe667b29 100644 --- a/ui/component/claimListDiscover/view.jsx +++ b/ui/component/claimListDiscover/view.jsx @@ -28,6 +28,7 @@ type Props = { meta?: Node, showNsfw: boolean, hideReposts: boolean, + fetchViewCount?: boolean, history: { action: string, push: (string) => void, replace: (string) => void }, location: { search: string, pathname: string }, claimSearchByQuery: { @@ -78,6 +79,8 @@ type Props = { showNoSourceClaims?: boolean, isChannel?: boolean, empty?: string, + claimsByUri: { [string]: any }, + doFetchViewCount: (claimIdCsv: string) => void, }; function ClaimListDiscover(props: Props) { @@ -94,6 +97,7 @@ function ClaimListDiscover(props: Props) { channelIds, showNsfw, hideReposts, + fetchViewCount, history, location, mutedUris, @@ -138,6 +142,8 @@ function ClaimListDiscover(props: Props) { isChannel = false, showNoSourceClaims, empty, + claimsByUri, + doFetchViewCount, } = props; const didNavigateForward = history.action === 'PUSH'; const { search } = location; @@ -518,6 +524,16 @@ function ClaimListDiscover(props: Props) { } } + function fetchViewCountForUris(uris) { + const claimIds = []; + uris.forEach((uri) => { + if (claimsByUri[uri]) { + claimIds.push(claimsByUri[uri].claim_id); + } + }); + doFetchViewCount(claimIds.join(',')); + } + // ************************************************************************** // ************************************************************************** @@ -547,6 +563,12 @@ function ClaimListDiscover(props: Props) { } }, [uris, claimSearchResult, finalUris, setFinalUris, liveLivestreamsFirst, livestreamSearchResult]); + React.useEffect(() => { + if (fetchViewCount) { + fetchViewCountForUris(finalUris); + } + }, [finalUris]); // eslint-disable-line react-hooks/exhaustive-deps + const headerToUse = header || ( )} + (isLivestream && ENABLE_NO_SOURCE_CLAIMS ? ( + __('Livestream') + ) : ( + <> + + + + ))} )} diff --git a/ui/component/claimPreviewTile/view.jsx b/ui/component/claimPreviewTile/view.jsx index 00539dfb2..64ee6c232 100644 --- a/ui/component/claimPreviewTile/view.jsx +++ b/ui/component/claimPreviewTile/view.jsx @@ -7,6 +7,7 @@ import UriIndicator from 'component/uriIndicator'; import TruncatedText from 'component/common/truncated-text'; import DateTime from 'component/dateTime'; import ChannelThumbnail from 'component/channelThumbnail'; +import FileViewCountInline from 'component/fileViewCountInline'; import SubscribeButton from 'component/subscribeButton'; import useGetThumbnail from 'effects/use-get-thumbnail'; import { formatLbryUrlForWeb } from 'util/url'; @@ -210,15 +211,11 @@ function ClaimPreviewTile(props: Props) { {!isChannel && (
- {isPlayable && ( - - )} + {isPlayable && }
{/* @if TARGET='app' */}
- {isStream && ( - - )} + {isStream && }
{/* @endif */} @@ -226,7 +223,6 @@ function ClaimPreviewTile(props: Props) {
- )} {isCollection && ( @@ -264,7 +260,10 @@ function ClaimPreviewTile(props: Props) {
- +
+ + +
)} diff --git a/ui/component/claimTilesDiscover/index.js b/ui/component/claimTilesDiscover/index.js index 8e210f9f0..a12963202 100644 --- a/ui/component/claimTilesDiscover/index.js +++ b/ui/component/claimTilesDiscover/index.js @@ -6,6 +6,7 @@ import { SETTINGS, selectClaimsByUri, } from 'lbry-redux'; +import { doFetchViewCount } from 'lbryinc'; import { doToggleTagFollowDesktop } from 'redux/actions/tags'; import { makeSelectClientSetting, selectShowMatureContent } from 'redux/selectors/settings'; import { selectModerationBlockList } from 'redux/selectors/comments'; @@ -25,6 +26,7 @@ const select = (state) => ({ const perform = { doClaimSearch, doToggleTagFollowDesktop, + doFetchViewCount, }; export default connect(select, perform)(ClaimListDiscover); diff --git a/ui/component/claimTilesDiscover/view.jsx b/ui/component/claimTilesDiscover/view.jsx index bcd755f18..79edd5a2e 100644 --- a/ui/component/claimTilesDiscover/view.jsx +++ b/ui/component/claimTilesDiscover/view.jsx @@ -92,6 +92,7 @@ type Props = { livestreamMap?: { [string]: any }, showNoSourceClaims?: boolean, renderProperties?: (Claim) => ?Node, + fetchViewCount?: boolean, // claim search options are below tags: Array, claimIds?: Array, @@ -117,6 +118,7 @@ type Props = { blockedUris: Array, // --- perform --- doClaimSearch: ({}) => void, + doFetchViewCount: (claimIdCsv: string) => void, }; function ClaimTilesDiscover(props: Props) { @@ -126,6 +128,7 @@ function ClaimTilesDiscover(props: Props) { claimsByUri, showNsfw, hideReposts, + fetchViewCount, // Below are options to pass that are forwarded to claim_search tags, channelIds, @@ -150,6 +153,7 @@ function ClaimTilesDiscover(props: Props) { pinUrls, prefixUris, showNoSourceClaims, + doFetchViewCount, } = props; const { location } = useHistory(); @@ -294,6 +298,16 @@ function ClaimTilesDiscover(props: Props) { return undefined; } + function fetchViewCountForUris(uris) { + const claimIds = []; + uris.forEach((uri) => { + if (claimsByUri[uri]) { + claimIds.push(claimsByUri[uri].claim_id); + } + }); + doFetchViewCount(claimIds.join(',')); + } + // ************************************************************************** // ************************************************************************** @@ -310,9 +324,14 @@ function ClaimTilesDiscover(props: Props) { React.useEffect(() => { if (JSON.stringify(prevUris) !== JSON.stringify(uris) && !shouldPerformSearch) { + // Stash new results for next render cycle: setPrevUris(uris); + // Fetch view count: + if (fetchViewCount) { + fetchViewCountForUris(uris); + } } - }, [shouldPerformSearch, prevUris, uris]); + }, [shouldPerformSearch, prevUris, uris]); // eslint-disable-line react-hooks/exhaustive-deps // ************************************************************************** // ************************************************************************** diff --git a/ui/component/fileViewCountInline/index.js b/ui/component/fileViewCountInline/index.js new file mode 100644 index 000000000..7bd072e79 --- /dev/null +++ b/ui/component/fileViewCountInline/index.js @@ -0,0 +1,13 @@ +import { connect } from 'react-redux'; +import { makeSelectClaimForUri } from 'lbry-redux'; +import { makeSelectViewCountForUri } from 'lbryinc'; +import FileViewCountInline from './view'; + +const select = (state, props) => { + return { + claim: makeSelectClaimForUri(props.uri)(state), + viewCount: makeSelectViewCountForUri(props.uri)(state), + }; +}; + +export default connect(select, null)(FileViewCountInline); diff --git a/ui/component/fileViewCountInline/view.jsx b/ui/component/fileViewCountInline/view.jsx new file mode 100644 index 000000000..78b49334f --- /dev/null +++ b/ui/component/fileViewCountInline/view.jsx @@ -0,0 +1,30 @@ +// @flow +import React from 'react'; + +type Props = { + uri: string, + isLivestream?: boolean, + // --- select --- + claim: ?StreamClaim, + viewCount: string, +}; + +export default function FileViewCountInline(props: Props) { + const { isLivestream, claim, viewCount } = props; + const formattedViewCount = Number(viewCount).toLocaleString(); + + if (!viewCount || (claim && claim.repost_url) || isLivestream) { + // (1) Currently, makeSelectViewCountForUri doesn't differentiate between + // unfetched view-count vs zero view-count. But since it's probably not + // ideal to highlight that a view has 0 count, let's just not show anything. + // (2) No idea how to get the repost src's claim ID from the repost claim, + // so hiding it for now. + return null; + } + + return ( + + {viewCount !== 1 ? __('%view_count% views', { view_count: formattedViewCount }) : __('1 view')} + + ); +} diff --git a/ui/scss/component/_claim-list.scss b/ui/scss/component/_claim-list.scss index 4304359f8..724c8f331 100644 --- a/ui/scss/component/_claim-list.scss +++ b/ui/scss/component/_claim-list.scss @@ -598,6 +598,18 @@ font-size: var(--font-body); } +.claim-tile__about--counts { + display: flex; + flex-wrap: wrap; +} + +.view_count { + &::after { + content: '•'; + margin: 0 var(--spacing-xxs); + } +} + .claim-preview__file-property-overlay { position: absolute; bottom: var(--spacing-xxs); @@ -681,14 +693,13 @@ // div that displays watch later button .claim-preview__hover-actions { - // if the user is using a mouse - @media (pointer: fine){ + @media (pointer: fine) { display: none; } // if the user doesn't have a pointer (mouse etc) hide watch later button - @media (pointer: none), (pointer:coarse) { + @media (pointer: none), (pointer: coarse) { display: none !important; } diff --git a/yarn.lock b/yarn.lock index e229530b2..407ee8e65 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10148,9 +10148,9 @@ lbry-redux@lbryio/lbry-redux#49b9db5aae8db84ec21231444bf8345d450812b2: reselect "^3.0.0" uuid "^8.3.1" -lbryinc@lbryio/lbryinc#8f9a58bfc8312a65614fd7327661cdcc502c4e59: +lbryinc@lbryio/lbryinc#0b4e41ef90d6347819dd3453f2f9398a5c1b4f36: version "0.0.1" - resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/8f9a58bfc8312a65614fd7327661cdcc502c4e59" + resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/0b4e41ef90d6347819dd3453f2f9398a5c1b4f36" dependencies: reselect "^3.0.0"