Livestream category improvements #7115
8 changed files with 12 additions and 202 deletions
|
@ -1,12 +1,10 @@
|
|||
import { connect } from 'react-redux';
|
||||
import ClaimList from './view';
|
||||
import { SETTINGS, selectClaimSearchByQuery, selectClaimsByUri } from 'lbry-redux';
|
||||
import { SETTINGS } from 'lbry-redux';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
|
||||
const select = (state) => ({
|
||||
searchInLanguage: makeSelectClientSetting(SETTINGS.SEARCH_IN_LANGUAGE)(state),
|
||||
claimSearchByQuery: selectClaimSearchByQuery(state),
|
||||
claimsByUri: selectClaimsByUri(state),
|
||||
});
|
||||
|
||||
export default connect(select)(ClaimList);
|
||||
|
|
|
@ -9,7 +9,6 @@ import { FormField } from 'component/common/form';
|
|||
import usePersistedState from 'effects/use-persisted-state';
|
||||
import debounce from 'util/debounce';
|
||||
import ClaimPreviewTile from 'component/claimPreviewTile';
|
||||
import { prioritizeActiveLivestreams } from 'component/claimTilesDiscover/view';
|
||||
|
||||
const DEBOUNCE_SCROLL_HANDLER_MS = 150;
|
||||
const SORT_NEW = 'new';
|
||||
|
@ -41,9 +40,6 @@ type Props = {
|
|||
hideMenu?: boolean,
|
||||
claimSearchByQuery: { [string]: Array<string> },
|
||||
claimsByUri: { [string]: any },
|
||||
liveLivestreamsFirst?: boolean,
|
||||
livestreamMap?: { [string]: any },
|
||||
searchOptions?: any,
|
||||
collectionId?: string,
|
||||
showNoSourceClaims?: boolean,
|
||||
onClick?: (e: any, claim?: ?Claim, index?: number) => void,
|
||||
|
@ -73,11 +69,6 @@ export default function ClaimList(props: Props) {
|
|||
renderProperties,
|
||||
searchInLanguage,
|
||||
hideMenu,
|
||||
claimSearchByQuery,
|
||||
claimsByUri,
|
||||
liveLivestreamsFirst,
|
||||
livestreamMap,
|
||||
searchOptions,
|
||||
collectionId,
|
||||
showNoSourceClaims,
|
||||
onClick,
|
||||
|
@ -87,23 +78,11 @@ export default function ClaimList(props: Props) {
|
|||
const timedOut = uris === null;
|
||||
const urisLength = (uris && uris.length) || 0;
|
||||
|
||||
const liveUris = [];
|
||||
if (liveLivestreamsFirst && livestreamMap) {
|
||||
prioritizeActiveLivestreams(uris, liveUris, livestreamMap, claimsByUri, claimSearchByQuery, searchOptions);
|
||||
}
|
||||
|
||||
const sortedUris = (urisLength > 0 && (currentSort === SORT_NEW ? uris : uris.slice().reverse())) || [];
|
||||
const noResultMsg = searchInLanguage
|
||||
? __('No results. Contents may be hidden by the Language filter.')
|
||||
: __('No results');
|
||||
|
||||
const resolveLive = (index) => {
|
||||
if (liveLivestreamsFirst && livestreamMap && index < liveUris.length) {
|
||||
return true;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
function handleSortChange() {
|
||||
setCurrentSort(currentSort === SORT_NEW ? SORT_OLD : SORT_NEW);
|
||||
}
|
||||
|
@ -138,13 +117,12 @@ export default function ClaimList(props: Props) {
|
|||
return tileLayout && !header ? (
|
||||
<section className="claim-grid">
|
||||
{urisLength > 0 &&
|
||||
uris.map((uri, index) => (
|
||||
uris.map((uri) => (
|
||||
<ClaimPreviewTile
|
||||
key={uri}
|
||||
uri={uri}
|
||||
showHiddenByUser={showHiddenByUser}
|
||||
properties={renderProperties}
|
||||
live={resolveLive(index)}
|
||||
collectionId={collectionId}
|
||||
showNoSourceClaims={showNoSourceClaims}
|
||||
/>
|
||||
|
@ -216,7 +194,6 @@ export default function ClaimList(props: Props) {
|
|||
// https://github.com/lbryio/lbry-redux/blob/master/src/redux/actions/publish.js#L74-L79
|
||||
return claim.name.length === 24 && !claim.name.includes(' ') && claim.value.author === 'Spee.ch';
|
||||
}}
|
||||
live={resolveLive(index)}
|
||||
onClick={(e, claim, index) => handleClaimClicked(e, claim, index)}
|
||||
/>
|
||||
</React.Fragment>
|
||||
|
|
|
@ -14,7 +14,6 @@ import ClaimPreviewTile from 'component/claimPreviewTile';
|
|||
import I18nMessage from 'component/i18nMessage';
|
||||
import ClaimListHeader from 'component/claimListHeader';
|
||||
import { useIsLargeScreen } from 'effects/use-screensize';
|
||||
import { getLivestreamOnlyOptions } from 'util/search';
|
||||
|
||||
type Props = {
|
||||
uris: Array<string>,
|
||||
|
@ -31,7 +30,6 @@ type Props = {
|
|||
includeSupportAction?: boolean,
|
||||
infiniteScroll?: Boolean,
|
||||
isChannel?: boolean,
|
||||
liveLivestreamsFirst?: boolean,
|
||||
personalView: boolean,
|
||||
showHeader: boolean,
|
||||
showHiddenByUser?: boolean,
|
||||
|
@ -64,7 +62,6 @@ type Props = {
|
|||
channelIds?: Array<string>,
|
||||
claimIds?: Array<string>,
|
||||
subscribedChannels: Array<Subscription>,
|
||||
livestreamMap?: { [string]: any },
|
||||
|
||||
header?: Node,
|
||||
headerLabel?: string | Node,
|
||||
|
@ -148,8 +145,6 @@ function ClaimListDiscover(props: Props) {
|
|||
releaseTime,
|
||||
scrollAnchor,
|
||||
showHiddenByUser = false,
|
||||
liveLivestreamsFirst,
|
||||
livestreamMap,
|
||||
hasSource,
|
||||
hasNoSource,
|
||||
isChannel = false,
|
||||
|
@ -400,11 +395,6 @@ function ClaimListDiscover(props: Props) {
|
|||
// claimSearchResult.splice(2, 0, fixUri);
|
||||
// }
|
||||
|
||||
const livestreamSearchKey = liveLivestreamsFirst
|
||||
? createNormalizedClaimSearchKey(getLivestreamOnlyOptions(options))
|
||||
: undefined;
|
||||
const livestreamSearchResult = livestreamSearchKey && claimSearchByQuery[livestreamSearchKey];
|
||||
|
||||
const [finalUris, setFinalUris] = React.useState(
|
||||
getFinalUrisInitialState(history.action === 'POP', claimSearchResult)
|
||||
);
|
||||
|
@ -571,10 +561,6 @@ function ClaimListDiscover(props: Props) {
|
|||
if (shouldPerformSearch) {
|
||||
const searchOptions = JSON.parse(optionsStringForEffect);
|
||||
doClaimSearch(searchOptions);
|
||||
|
||||
if (liveLivestreamsFirst && options.page === 1) {
|
||||
doClaimSearch(getLivestreamOnlyOptions(searchOptions));
|
||||
}
|
||||
}
|
||||
}, [doClaimSearch, shouldPerformSearch, optionsStringForEffect, forceRefresh]);
|
||||
|
||||
|
@ -586,12 +572,12 @@ function ClaimListDiscover(props: Props) {
|
|||
}
|
||||
} else {
|
||||
// Wait until all queries are done before updating the uris to avoid layout shifts.
|
||||
const pending = claimSearchResult === undefined || (liveLivestreamsFirst && livestreamSearchResult === undefined);
|
||||
const pending = claimSearchResult === undefined;
|
||||
if (!pending && !urisEqual(claimSearchResult, finalUris)) {
|
||||
setFinalUris(claimSearchResult);
|
||||
}
|
||||
}
|
||||
}, [uris, claimSearchResult, finalUris, setFinalUris, liveLivestreamsFirst, livestreamSearchResult]);
|
||||
}, [uris, claimSearchResult, finalUris, setFinalUris]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (fetchViewCount) {
|
||||
|
@ -645,8 +631,6 @@ function ClaimListDiscover(props: Props) {
|
|||
includeSupportAction={includeSupportAction}
|
||||
injectedItem={injectedItem}
|
||||
showHiddenByUser={showHiddenByUser}
|
||||
liveLivestreamsFirst={liveLivestreamsFirst}
|
||||
livestreamMap={livestreamMap}
|
||||
searchOptions={options}
|
||||
showNoSourceClaims={showNoSourceClaims}
|
||||
empty={empty}
|
||||
|
@ -679,8 +663,6 @@ function ClaimListDiscover(props: Props) {
|
|||
includeSupportAction={includeSupportAction}
|
||||
injectedItem={injectedItem}
|
||||
showHiddenByUser={showHiddenByUser}
|
||||
liveLivestreamsFirst={liveLivestreamsFirst}
|
||||
livestreamMap={livestreamMap}
|
||||
searchOptions={options}
|
||||
showNoSourceClaims={hasNoSource || showNoSourceClaims}
|
||||
empty={empty}
|
||||
|
|
|
@ -6,90 +6,11 @@ import React from 'react';
|
|||
import { createNormalizedClaimSearchKey, MATURE_TAGS, splitBySeparator } from 'lbry-redux';
|
||||
import ClaimPreviewTile from 'component/claimPreviewTile';
|
||||
import { useHistory } from 'react-router';
|
||||
import { getLivestreamOnlyOptions } from 'util/search';
|
||||
|
||||
/**
|
||||
* Updates 'uris' by adding and/or moving active livestreams to the front of
|
||||
* list.
|
||||
* 'liveUris' is also updated with any entries that were moved to the
|
||||
* front, for convenience.
|
||||
*
|
||||
* @param uris [Ref]
|
||||
* @param liveUris [Ref]
|
||||
* @param livestreamMap
|
||||
* @param claimsByUri
|
||||
* @param claimSearchByQuery
|
||||
* @param options
|
||||
*/
|
||||
export function prioritizeActiveLivestreams(
|
||||
uris: Array<string>,
|
||||
liveUris: Array<string>,
|
||||
livestreamMap: { [string]: any },
|
||||
claimsByUri: { [string]: any },
|
||||
claimSearchByQuery: { [string]: Array<string> },
|
||||
options: any
|
||||
) {
|
||||
if (!livestreamMap || !uris) return;
|
||||
|
||||
const claimIsLive = (claim, liveChannelIds) => {
|
||||
// This function relies on:
|
||||
// 1. Only 1 actual livestream per channel (i.e. all other livestream-claims
|
||||
// for that channel actually point to the same source).
|
||||
// 2. 'liveChannelIds' needs to be pruned after being accounted for,
|
||||
// otherwise all livestream-claims will be "live" (we'll only take the
|
||||
// latest one as "live" ).
|
||||
return (
|
||||
claim &&
|
||||
claim.value_type === 'stream' &&
|
||||
claim.value.source === undefined &&
|
||||
claim.signing_channel &&
|
||||
liveChannelIds.includes(claim.signing_channel.claim_id)
|
||||
);
|
||||
};
|
||||
|
||||
let liveChannelIds = Object.keys(livestreamMap);
|
||||
|
||||
// 1. Collect active livestreams from the primary search to put in front.
|
||||
uris.forEach((uri) => {
|
||||
const claim = claimsByUri[uri];
|
||||
if (claimIsLive(claim, liveChannelIds)) {
|
||||
liveUris.push(uri);
|
||||
// This live channel has been accounted for, so remove it.
|
||||
liveChannelIds.splice(liveChannelIds.indexOf(claim.signing_channel.claim_id), 1);
|
||||
}
|
||||
});
|
||||
|
||||
// 2. Now, repeat on the secondary search.
|
||||
if (options) {
|
||||
const livestreamsOnlySearchCacheQuery = createNormalizedClaimSearchKey(getLivestreamOnlyOptions(options));
|
||||
const livestreamsOnlyUris = claimSearchByQuery[livestreamsOnlySearchCacheQuery];
|
||||
if (livestreamsOnlyUris) {
|
||||
livestreamsOnlyUris.forEach((uri) => {
|
||||
const claim = claimsByUri[uri];
|
||||
if (!uris.includes(uri) && claimIsLive(claim, liveChannelIds)) {
|
||||
liveUris.push(uri);
|
||||
// This live channel has been accounted for, so remove it.
|
||||
liveChannelIds.splice(liveChannelIds.indexOf(claim.signing_channel.claim_id), 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Finalize uris by putting live livestreams in front.
|
||||
const newUris = liveUris.concat(uris.filter((uri) => !liveUris.includes(uri)));
|
||||
uris.splice(0, uris.length, ...newUris);
|
||||
}
|
||||
|
||||
// ****************************************************************************
|
||||
// ClaimTilesDiscover
|
||||
// ****************************************************************************
|
||||
|
||||
type Props = {
|
||||
prefixUris?: Array<string>,
|
||||
pinUrls?: Array<string>,
|
||||
uris: Array<string>,
|
||||
liveLivestreamsFirst?: boolean,
|
||||
livestreamMap?: { [string]: any },
|
||||
showNoSourceClaims?: boolean,
|
||||
renderProperties?: (Claim) => ?Node,
|
||||
fetchViewCount?: boolean,
|
||||
|
@ -148,8 +69,6 @@ function ClaimTilesDiscover(props: Props) {
|
|||
renderProperties,
|
||||
blockedUris,
|
||||
mutedUris,
|
||||
liveLivestreamsFirst,
|
||||
livestreamMap,
|
||||
pinUrls,
|
||||
prefixUris,
|
||||
showNoSourceClaims,
|
||||
|
@ -163,7 +82,7 @@ function ClaimTilesDiscover(props: Props) {
|
|||
const mutedAndBlockedChannelIds = Array.from(
|
||||
new Set(mutedUris.concat(blockedUris).map((uri) => splitBySeparator(uri)[1]))
|
||||
);
|
||||
const liveUris = [];
|
||||
|
||||
let streamTypesParam;
|
||||
if (streamTypes) {
|
||||
streamTypesParam = streamTypes;
|
||||
|
@ -245,28 +164,16 @@ function ClaimTilesDiscover(props: Props) {
|
|||
options.claim_ids = claimIds;
|
||||
}
|
||||
|
||||
const mainSearchKey = createNormalizedClaimSearchKey(options);
|
||||
const livestreamSearchKey = liveLivestreamsFirst
|
||||
? createNormalizedClaimSearchKey(getLivestreamOnlyOptions(options))
|
||||
: undefined;
|
||||
const searchKey = createNormalizedClaimSearchKey(options);
|
||||
const isLoading = fetchingClaimSearchByQuery[searchKey];
|
||||
|
||||
let uris = (prefixUris || []).concat(claimSearchByQuery[mainSearchKey] || []);
|
||||
|
||||
const isLoading = fetchingClaimSearchByQuery[mainSearchKey];
|
||||
|
||||
if (liveLivestreamsFirst && livestreamMap && !isLoading) {
|
||||
prioritizeActiveLivestreams(uris, liveUris, livestreamMap, claimsByUri, claimSearchByQuery, options);
|
||||
}
|
||||
let uris = (prefixUris || []).concat(claimSearchByQuery[searchKey] || []);
|
||||
|
||||
// Don't use the query from createNormalizedClaimSearchKey for the effect since that doesn't include page & release_time
|
||||
const optionsStringForEffect = JSON.stringify(options);
|
||||
const shouldPerformSearch = !isLoading && uris.length === 0;
|
||||
|
||||
if (
|
||||
prefixUris === undefined &&
|
||||
(claimSearchByQuery[mainSearchKey] === undefined ||
|
||||
(livestreamSearchKey && claimSearchByQuery[livestreamSearchKey] === undefined))
|
||||
) {
|
||||
if (prefixUris === undefined && claimSearchByQuery[searchKey] === undefined) {
|
||||
// This is a new query and we don't have results yet ...
|
||||
if (prevUris.length !== 0) {
|
||||
// ... but we have previous results. Use it until new results are here.
|
||||
|
@ -288,16 +195,6 @@ function ClaimTilesDiscover(props: Props) {
|
|||
modifiedUris.splice(2, 0, ...fixUris);
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// **************************************************************************
|
||||
|
||||
function resolveLive(index) {
|
||||
if (liveLivestreamsFirst && livestreamMap && index < liveUris.length) {
|
||||
return true;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function fetchViewCountForUris(uris) {
|
||||
const claimIds = [];
|
||||
|
||||
|
@ -314,19 +211,12 @@ function ClaimTilesDiscover(props: Props) {
|
|||
}
|
||||
}
|
||||
|
||||
// **************************************************************************
|
||||
// **************************************************************************
|
||||
|
||||
React.useEffect(() => {
|
||||
if (shouldPerformSearch) {
|
||||
const searchOptions = JSON.parse(optionsStringForEffect);
|
||||
doClaimSearch(searchOptions);
|
||||
|
||||
if (liveLivestreamsFirst) {
|
||||
doClaimSearch(getLivestreamOnlyOptions(searchOptions));
|
||||
}
|
||||
}
|
||||
}, [doClaimSearch, shouldPerformSearch, optionsStringForEffect, liveLivestreamsFirst]);
|
||||
}, [doClaimSearch, shouldPerformSearch, optionsStringForEffect]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (JSON.stringify(prevUris) !== JSON.stringify(uris) && !shouldPerformSearch) {
|
||||
|
@ -345,13 +235,12 @@ function ClaimTilesDiscover(props: Props) {
|
|||
return (
|
||||
<ul className="claim-grid">
|
||||
{modifiedUris && modifiedUris.length
|
||||
? modifiedUris.map((uri, index) => (
|
||||
? modifiedUris.map((uri) => (
|
||||
<ClaimPreviewTile
|
||||
showNoSourceClaims={hasNoSource || showNoSourceClaims}
|
||||
key={uri}
|
||||
uri={uri}
|
||||
properties={renderProperties}
|
||||
live={resolveLive(index)}
|
||||
/>
|
||||
))
|
||||
: new Array(pageSize)
|
||||
|
|
|
@ -9,7 +9,6 @@ import ClaimListDiscover from 'component/claimListDiscover';
|
|||
import Page from 'component/page';
|
||||
import Button from 'component/button';
|
||||
import Icon from 'component/common/icon';
|
||||
import useGetLivestreams from 'effects/use-get-livestreams';
|
||||
import { splitBySeparator } from 'lbry-redux';
|
||||
|
||||
type Props = {
|
||||
|
@ -20,7 +19,6 @@ type Props = {
|
|||
function ChannelsFollowingPage(props: Props) {
|
||||
const { subscribedChannels, tileLayout } = props;
|
||||
const hasSubsribedChannels = subscribedChannels.length > 0;
|
||||
const { livestreamMap } = useGetLivestreams();
|
||||
|
||||
return !hasSubsribedChannels ? (
|
||||
<ChannelsFollowingDiscoverPage />
|
||||
|
@ -46,8 +44,6 @@ function ChannelsFollowingPage(props: Props) {
|
|||
navigate={`/$/${PAGES.CHANNELS_FOLLOWING_DISCOVER}`}
|
||||
/>
|
||||
}
|
||||
liveLivestreamsFirst
|
||||
livestreamMap={livestreamMap}
|
||||
showNoSourceClaims={ENABLE_NO_SOURCE_CLAIMS}
|
||||
hasSource
|
||||
/>
|
||||
|
|
|
@ -15,7 +15,6 @@ import Icon from 'component/common/icon';
|
|||
import Ads from 'web/component/ads';
|
||||
import LbcSymbol from 'component/common/lbc-symbol';
|
||||
import I18nMessage from 'component/i18nMessage';
|
||||
import useGetLivestreams from 'effects/use-get-livestreams';
|
||||
import moment from 'moment';
|
||||
|
||||
type Props = {
|
||||
|
@ -45,7 +44,6 @@ function DiscoverPage(props: Props) {
|
|||
const buttonRef = useRef();
|
||||
const isHovering = useHover(buttonRef);
|
||||
const isMobile = useIsMobile();
|
||||
const { livestreamMap } = useGetLivestreams();
|
||||
|
||||
const urlParams = new URLSearchParams(search);
|
||||
const claimType = urlParams.get('claim_type');
|
||||
|
@ -172,8 +170,6 @@ function DiscoverPage(props: Props) {
|
|||
)
|
||||
)
|
||||
}
|
||||
liveLivestreamsFirst
|
||||
livestreamMap={livestreamMap}
|
||||
hasSource
|
||||
showNoSourceClaims={ENABLE_NO_SOURCE_CLAIMS}
|
||||
/>
|
||||
|
|
|
@ -9,7 +9,6 @@ import ClaimTilesDiscover from 'component/claimTilesDiscover';
|
|||
import ClaimPreviewTile from 'component/claimPreviewTile';
|
||||
import Icon from 'component/common/icon';
|
||||
import WaitUntilOnPage from 'component/common/wait-until-on-page';
|
||||
import useGetLivestreams from 'effects/use-get-livestreams';
|
||||
import { GetLinksData } from 'util/buildHomepage';
|
||||
|
||||
// @if TARGET='web'
|
||||
|
@ -30,7 +29,6 @@ function HomePage(props: Props) {
|
|||
const showPersonalizedChannels = (authenticated || !IS_WEB) && subscribedChannels && subscribedChannels.length > 0;
|
||||
const showPersonalizedTags = (authenticated || !IS_WEB) && followedTags && followedTags.length > 0;
|
||||
const showIndividualTags = showPersonalizedTags && followedTags.length < 5;
|
||||
const { livestreamMap } = useGetLivestreams();
|
||||
|
||||
const rowData: Array<RowDataItem> = GetLinksData(
|
||||
homepageData,
|
||||
|
@ -53,14 +51,7 @@ function HomePage(props: Props) {
|
|||
</ul>
|
||||
);
|
||||
const claimTiles = (
|
||||
<ClaimTilesDiscover
|
||||
{...options}
|
||||
liveLivestreamsFirst
|
||||
livestreamMap={livestreamMap}
|
||||
showNoSourceClaims={ENABLE_NO_SOURCE_CLAIMS}
|
||||
hasSource
|
||||
pinUrls={pinUrls}
|
||||
/>
|
||||
<ClaimTilesDiscover {...options} showNoSourceClaims={ENABLE_NO_SOURCE_CLAIMS} hasSource pinUrls={pinUrls} />
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -21,25 +21,6 @@ export function createNormalizedSearchKey(query: string) {
|
|||
return normalizedQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "livestream only" version of the given 'options'.
|
||||
*
|
||||
* Currently, the 'has_source' attribute is being used to identify livestreams.
|
||||
*
|
||||
* @param options
|
||||
* @returns {*}
|
||||
*/
|
||||
export function getLivestreamOnlyOptions(options: any) {
|
||||
const newOptions = Object.assign({}, options);
|
||||
delete newOptions.has_source;
|
||||
delete newOptions.stream_types;
|
||||
newOptions.has_no_source = true;
|
||||
newOptions.claim_type = ['stream'];
|
||||
newOptions.page_size = 50;
|
||||
newOptions.order_by = ['release_time'];
|
||||
return newOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* getUriForSearchTerm
|
||||
* @param term
|
||||
|
|
Loading…
Reference in a new issue