Recommended changes #7089
8 changed files with 149 additions and 193 deletions
|
@ -1,31 +1,24 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { makeSelectClaimIsNsfw, makeSelectClaimForUri } from 'lbry-redux';
|
||||
import { doRecommendationUpdate, doRecommendationClicked } from 'redux/actions/content';
|
||||
import { makeSelectClaimForUri } from 'lbry-redux';
|
||||
import { doFetchRecommendedContent } from 'redux/actions/search';
|
||||
import { makeSelectRecommendedContentForUri, selectIsSearching } from 'redux/selectors/search';
|
||||
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||
import { makeSelectNextUnplayedRecommended } from 'redux/selectors/content';
|
||||
import RecommendedContent from './view';
|
||||
|
||||
const select = (state, props) => {
|
||||
const claim = makeSelectClaimForUri(props.uri)(state);
|
||||
const { claim_id: claimId } = claim;
|
||||
const recommendedContentUris = makeSelectRecommendedContentForUri(props.uri)(state);
|
||||
const nextRecommendedUri = recommendedContentUris && recommendedContentUris[0];
|
||||
|
||||
return {
|
||||
mature: makeSelectClaimIsNsfw(props.uri)(state),
|
||||
recommendedContentUris: makeSelectRecommendedContentForUri(props.uri)(state),
|
||||
nextRecommendedUri: makeSelectNextUnplayedRecommended(props.uri)(state),
|
||||
isSearching: selectIsSearching(state),
|
||||
isAuthenticated: selectUserVerifiedEmail(state),
|
||||
claim,
|
||||
claimId,
|
||||
recommendedContentUris,
|
||||
nextRecommendedUri,
|
||||
isSearching: selectIsSearching(state),
|
||||
isAuthenticated: selectUserVerifiedEmail(state),
|
||||
};
|
||||
};
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
doFetchRecommendedContent: (uri, mature) => dispatch(doFetchRecommendedContent(uri, mature)),
|
||||
doRecommendationUpdate: (claimId, urls, id, parentId) =>
|
||||
dispatch(doRecommendationUpdate(claimId, urls, id, parentId)),
|
||||
doRecommendationClicked: (claimId, index) => dispatch(doRecommendationClicked(claimId, index)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(RecommendedContent);
|
||||
export default connect(select, { doFetchRecommendedContent })(RecommendedContent);
|
||||
|
|
|
@ -18,11 +18,9 @@ type Props = {
|
|||
recommendedContentUris: Array<string>,
|
||||
nextRecommendedUri: string,
|
||||
isSearching: boolean,
|
||||
doFetchRecommendedContent: (string, boolean) => void,
|
||||
mature: boolean,
|
||||
doFetchRecommendedContent: (string) => void,
|
||||
isAuthenticated: boolean,
|
||||
claim: ?StreamClaim,
|
||||
doRecommendationUpdate: (claimId: string, urls: Array<string>, id: string, parentId: string) => void,
|
||||
claimId: string,
|
||||
};
|
||||
|
||||
|
@ -30,7 +28,6 @@ export default React.memo<Props>(function RecommendedContent(props: Props) {
|
|||
const {
|
||||
uri,
|
||||
doFetchRecommendedContent,
|
||||
mature,
|
||||
recommendedContentUris,
|
||||
nextRecommendedUri,
|
||||
isSearching,
|
||||
|
@ -39,53 +36,29 @@ export default React.memo<Props>(function RecommendedContent(props: Props) {
|
|||
claimId,
|
||||
} = props;
|
||||
const [viewMode, setViewMode] = React.useState(VIEW_ALL_RELATED);
|
||||
const [recommendationUrls, setRecommendationUrls] = React.useState();
|
||||
const signingChannel = claim && claim.signing_channel;
|
||||
const channelName = signingChannel ? signingChannel.name : null;
|
||||
const isMobile = useIsMobile();
|
||||
const isMedium = useIsMediumScreen();
|
||||
const { onRecsLoaded: onRecommendationsLoaded, onClickedRecommended: onRecommendationClicked } = RecSys;
|
||||
React.useEffect(() => {
|
||||
function moveAutoplayNextItemToTop(recommendedContent) {
|
||||
let newList = recommendedContent;
|
||||
if (newList) {
|
||||
const index = newList.indexOf(nextRecommendedUri);
|
||||
if (index > 0) {
|
||||
const a = newList[0];
|
||||
newList[0] = nextRecommendedUri;
|
||||
newList[index] = a;
|
||||
}
|
||||
}
|
||||
return newList;
|
||||
}
|
||||
|
||||
function listEq(prev, next) {
|
||||
if (prev && next) {
|
||||
return prev.length === next.length && prev.every((value, index) => value === next[index]);
|
||||
} else {
|
||||
return prev === next;
|
||||
}
|
||||
}
|
||||
|
||||
const newRecommendationUrls = moveAutoplayNextItemToTop(recommendedContentUris);
|
||||
|
||||
if (claim && !listEq(recommendationUrls, newRecommendationUrls)) {
|
||||
setRecommendationUrls(newRecommendationUrls);
|
||||
}
|
||||
}, [recommendedContentUris, nextRecommendedUri, recommendationUrls, setRecommendationUrls, claim]);
|
||||
|
||||
React.useEffect(() => {
|
||||
doFetchRecommendedContent(uri, mature);
|
||||
}, [uri, mature, doFetchRecommendedContent]);
|
||||
doFetchRecommendedContent(uri);
|
||||
}, [uri, doFetchRecommendedContent]);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Right now we only want to record the recs if they actually saw them.
|
||||
if (recommendationUrls && recommendationUrls.length && nextRecommendedUri && viewMode === VIEW_ALL_RELATED) {
|
||||
onRecommendationsLoaded(claimId, recommendationUrls);
|
||||
if (
|
||||
recommendedContentUris &&
|
||||
recommendedContentUris.length &&
|
||||
nextRecommendedUri &&
|
||||
viewMode === VIEW_ALL_RELATED
|
||||
) {
|
||||
onRecommendationsLoaded(claimId, recommendedContentUris);
|
||||
}
|
||||
}, [recommendationUrls, onRecommendationsLoaded, claimId, nextRecommendedUri, viewMode]);
|
||||
}, [recommendedContentUris, onRecommendationsLoaded, claimId, nextRecommendedUri, viewMode]);
|
||||
|
||||
function handleRecommendationClicked(e, clickedClaim, index: number) {
|
||||
function handleRecommendationClicked(e, clickedClaim) {
|
||||
if (claim) {
|
||||
onRecommendationClicked(claim.claim_id, clickedClaim.claim_id);
|
||||
}
|
||||
|
@ -124,7 +97,7 @@ export default React.memo<Props>(function RecommendedContent(props: Props) {
|
|||
<ClaimList
|
||||
type="small"
|
||||
loading={isSearching}
|
||||
uris={recommendationUrls}
|
||||
uris={recommendedContentUris}
|
||||
hideMenu={isMobile}
|
||||
injectedItem={SHOW_ADS && IS_WEB && !isAuthenticated && <Ads small type={'video'} />}
|
||||
empty={__('No related content found')}
|
||||
|
@ -164,7 +137,6 @@ function areEqual(prevProps: Props, nextProps: Props) {
|
|||
a.nextRecommendedUri !== b.nextRecommendedUri ||
|
||||
a.isAuthenticated !== b.isAuthenticated ||
|
||||
a.isSearching !== b.isSearching ||
|
||||
a.mature !== b.mature ||
|
||||
(a.recommendedContentUris && !b.recommendedContentUris) ||
|
||||
(!a.recommendedContentUris && b.recommendedContentUris) ||
|
||||
(a.claim && !b.claim) ||
|
||||
|
|
|
@ -16,12 +16,8 @@ import {
|
|||
} from 'redux/actions/app';
|
||||
import { selectVolume, selectMute } from 'redux/selectors/app';
|
||||
import { savePosition, clearPosition, doPlayUri, doSetPlayingUri } from 'redux/actions/content';
|
||||
import {
|
||||
makeSelectContentPositionForUri,
|
||||
makeSelectIsPlayerFloating,
|
||||
makeSelectNextUnplayedRecommended,
|
||||
selectPlayingUri,
|
||||
} from 'redux/selectors/content';
|
||||
import { makeSelectContentPositionForUri, makeSelectIsPlayerFloating, selectPlayingUri } from 'redux/selectors/content';
|
||||
import { makeSelectRecommendedContentForUri } from 'redux/selectors/search';
|
||||
import VideoViewer from './view';
|
||||
import { withRouter } from 'react-router';
|
||||
import { doClaimEligiblePurchaseRewards } from 'redux/actions/rewards';
|
||||
|
@ -47,7 +43,8 @@ const select = (state, props) => {
|
|||
nextRecommendedUri = makeSelectNextUrlForCollectionAndUrl(collectionId, uri)(state);
|
||||
previousListUri = makeSelectPreviousUrlForCollectionAndUrl(collectionId, uri)(state);
|
||||
} else {
|
||||
nextRecommendedUri = makeSelectNextUnplayedRecommended(uri)(state);
|
||||
const recommendedContent = makeSelectRecommendedContentForUri(uri)(state);
|
||||
nextRecommendedUri = recommendedContent && recommendedContent[0];
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -13,9 +13,11 @@ export const SEARCH_TYPES = {
|
|||
export const SEARCH_OPTIONS = {
|
||||
RESULT_COUNT: 'size',
|
||||
CLAIM_TYPE: 'claimType',
|
||||
RELATED_TO: 'related_to',
|
||||
INCLUDE_FILES: 'file',
|
||||
INCLUDE_CHANNELS: 'channel',
|
||||
INCLUDE_FILES_AND_CHANNELS: 'file,channel',
|
||||
INCLUDE_MATURE: 'nsfw',
|
||||
now I did 👍 now I did 👍
|
||||
MEDIA_AUDIO: 'audio',
|
||||
MEDIA_VIDEO: 'video',
|
||||
MEDIA_TEXT: 'text',
|
||||
|
@ -25,6 +27,7 @@ export const SEARCH_OPTIONS = {
|
|||
SORT_ACCENDING: '^release_time',
|
||||
SORT_DESCENDING: 'release_time',
|
||||
EXACT: 'exact',
|
||||
PRICE_FILTER_FREE: 'free_only',
|
||||
TIME_FILTER: 'time_filter',
|
||||
TIME_FILTER_LAST_HOUR: 'lasthour',
|
||||
TIME_FILTER_TODAY: 'today',
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
// @flow
|
||||
import * as ACTIONS from 'constants/action_types';
|
||||
import { SEARCH_OPTIONS } from 'constants/search';
|
||||
import { buildURI, doResolveUris, batchActions, isURIValid, makeSelectClaimForUri } from 'lbry-redux';
|
||||
import { selectShowMatureContent } from 'redux/selectors/settings';
|
||||
import {
|
||||
buildURI,
|
||||
doResolveUris,
|
||||
batchActions,
|
||||
isURIValid,
|
||||
makeSelectClaimForUri,
|
||||
makeSelectClaimIsNsfw,
|
||||
} from 'lbry-redux';
|
||||
import { makeSelectSearchUrisForQuery, selectSearchValue } from 'redux/selectors/search';
|
||||
import handleFetchResponse from 'util/handle-fetch';
|
||||
import { getSearchQueryString } from 'util/query-params';
|
||||
|
@ -125,21 +133,26 @@ export const doUpdateSearchOptions = (newOptions: SearchOptions, additionalOptio
|
|||
}
|
||||
};
|
||||
|
||||
export const doFetchRecommendedContent = (uri: string, mature: boolean) => (dispatch: Dispatch, getState: GetState) => {
|
||||
export const doFetchRecommendedContent = (uri: string) => (dispatch: Dispatch, getState: GetState) => {
|
||||
const state = getState();
|
||||
const claim = makeSelectClaimForUri(uri)(state);
|
||||
const matureEnabled = selectShowMatureContent(state);
|
||||
const claimIsMature = makeSelectClaimIsNsfw(uri)(state);
|
||||
|
||||
if (claim && claim.value && claim.claim_id) {
|
||||
const options: SearchOptions = { size: 20, related_to: claim.claim_id, isBackgroundSearch: true };
|
||||
if (!mature) {
|
||||
options['nsfw'] = false;
|
||||
}
|
||||
const options: SearchOptions = { size: 20, nsfw: matureEnabled, isBackgroundSearch: true };
|
||||
|
||||
if (SIMPLE_SITE) {
|
||||
options[SEARCH_OPTIONS.CLAIM_TYPE] = SEARCH_OPTIONS.INCLUDE_FILES;
|
||||
options[SEARCH_OPTIONS.MEDIA_VIDEO] = true;
|
||||
options[SEARCH_OPTIONS.PRICE_FILTER_FREE] = true;
|
||||
}
|
||||
if (matureEnabled || !claimIsMature) {
|
||||
options[SEARCH_OPTIONS.RELATED_TO] = claim.claim_id;
|
||||
}
|
||||
|
||||
const { title } = claim.value;
|
||||
|
||||
if (title && options) {
|
||||
dispatch(doSearch(title, options));
|
||||
}
|
||||
|
|
|
@ -7,13 +7,10 @@ import {
|
|||
makeSelectClaimIsMine,
|
||||
makeSelectMediaTypeForUri,
|
||||
selectBalance,
|
||||
parseURI,
|
||||
makeSelectContentTypeForUri,
|
||||
makeSelectFileNameForUri,
|
||||
} from 'lbry-redux';
|
||||
import { makeSelectRecommendedContentForUri } from 'redux/selectors/search';
|
||||
import { selectMutedChannels } from 'redux/selectors/blocked';
|
||||
import { selectAllCostInfoByUri, makeSelectCostInfoForUri } from 'lbryinc';
|
||||
import { makeSelectCostInfoForUri } from 'lbryinc';
|
||||
import { selectShowMatureContent } from 'redux/selectors/settings';
|
||||
import * as RENDER_MODES from 'constants/file_render_modes';
|
||||
import path from 'path';
|
||||
|
@ -35,13 +32,14 @@ export const makeSelectIsPlaying = (uri: string) =>
|
|||
|
||||
export const makeSelectIsPlayerFloating = (location: UrlLocation) =>
|
||||
createSelector(selectPrimaryUri, selectPlayingUri, (primaryUri, playingUri) => {
|
||||
to me hasSource gets confused with the idea of whether a claim has a source as in livestreams. to me hasSource gets confused with the idea of whether a claim has a source as in livestreams.
hasSecondarySource maybe.
|
||||
const hasSecondarySource = playingUri && (playingUri.source === 'comment' || playingUri.source === 'markdown');
|
||||
const isInlineSecondaryPlayer =
|
||||
playingUri &&
|
||||
playingUri.uri !== primaryUri &&
|
||||
location.pathname === playingUri.pathname &&
|
||||
(playingUri.source === 'comment' || playingUri.source === 'markdown');
|
||||
playingUri && playingUri.uri !== primaryUri && location.pathname === playingUri.pathname && hasSecondarySource;
|
||||
|
||||
if ((playingUri && playingUri.primaryUri === primaryUri) || isInlineSecondaryPlayer) {
|
||||
if (
|
||||
(playingUri && (hasSecondarySource ? playingUri.primaryUri === primaryUri : playingUri.uri === primaryUri)) ||
|
||||
isInlineSecondaryPlayer
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -77,78 +75,6 @@ export const makeSelectHistoryForUri = (uri: string) =>
|
|||
export const makeSelectHasVisitedUri = (uri: string) =>
|
||||
createSelector(makeSelectHistoryForUri(uri), (history) => Boolean(history));
|
||||
|
||||
export const makeSelectNextUnplayedRecommended = (uri: string) =>
|
||||
createSelector(
|
||||
makeSelectRecommendedContentForUri(uri),
|
||||
selectHistory,
|
||||
selectClaimsByUri,
|
||||
selectAllCostInfoByUri,
|
||||
selectMutedChannels,
|
||||
(
|
||||
recommendedForUri: Array<string>,
|
||||
history: Array<{ uri: string }>,
|
||||
claimsByUri: { [string]: ?Claim },
|
||||
costInfoByUri: { [string]: { cost: 0 | string } },
|
||||
blockedChannels: Array<string>
|
||||
) => {
|
||||
if (recommendedForUri) {
|
||||
// Make sure we don't autoplay paid content, channels, or content from blocked channels
|
||||
for (let i = 0; i < recommendedForUri.length; i++) {
|
||||
const recommendedUri = recommendedForUri[i];
|
||||
const claim = claimsByUri[recommendedUri];
|
||||
|
||||
if (!claim) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { isChannel } = parseURI(recommendedUri);
|
||||
if (isChannel) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const costInfo = costInfoByUri[recommendedUri];
|
||||
if (!costInfo || costInfo.cost !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We already check if it's a channel above
|
||||
// $FlowFixMe
|
||||
const isVideo = claim.value && claim.value.stream_type === 'video';
|
||||
// $FlowFixMe
|
||||
const isAudio = claim.value && claim.value.stream_type === 'audio';
|
||||
if (!isVideo && !isAudio) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const channel = claim && claim.signing_channel;
|
||||
if (channel && blockedChannels.some((blockedUri) => blockedUri === channel.permanent_url)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const recommendedUriInfo = parseURI(recommendedUri);
|
||||
const recommendedUriShort = recommendedUriInfo.claimName + '#' + recommendedUriInfo.claimId.substring(0, 1);
|
||||
|
||||
if (claimsByUri[uri] && claimsByUri[uri].claim_id === recommendedUriInfo.claimId) {
|
||||
// Skip myself (same claim ID)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
!history.some((h) => {
|
||||
const directMatch = h.uri === recommendedForUri[i];
|
||||
const shortUriMatch = h.uri.includes(recommendedUriShort);
|
||||
const idMatch = claimsByUri[h.uri] && claimsByUri[h.uri].claim_id === recommendedUriInfo.claimId;
|
||||
|
||||
return directMatch || shortUriMatch || idMatch;
|
||||
})
|
||||
) {
|
||||
return recommendedForUri[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const selectRecentHistory = createSelector(selectHistory, (history) => {
|
||||
return history.slice(0, RECENT_HISTORY_AMOUNT);
|
||||
});
|
||||
|
|
|
@ -1,18 +1,23 @@
|
|||
// @flow
|
||||
import { getSearchQueryString } from 'util/query-params';
|
||||
import { selectShowMatureContent } from 'redux/selectors/settings';
|
||||
import { SEARCH_OPTIONS } from 'constants/search';
|
||||
import {
|
||||
parseURI,
|
||||
selectClaimsByUri,
|
||||
makeSelectClaimForUri,
|
||||
makeSelectClaimForClaimId,
|
||||
makeSelectClaimIsNsfw,
|
||||
buildURI,
|
||||
isClaimNsfw,
|
||||
makeSelectPendingClaimForUri,
|
||||
makeSelectIsUriResolving,
|
||||
} from 'lbry-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { createNormalizedSearchKey } from 'util/search';
|
||||
import { selectMutedChannels } from 'redux/selectors/blocked';
|
||||
import { selectHistory } from 'redux/selectors/content';
|
||||
import { selectAllCostInfoByUri } from 'lbryinc';
|
||||
import { SIMPLE_SITE } from 'config';
|
||||
|
||||
type State = { search: SearchState };
|
||||
|
||||
|
@ -38,14 +43,12 @@ export const selectHasReachedMaxResultsLength: (state: State) => { [boolean]: Ar
|
|||
);
|
||||
|
||||
export const makeSelectSearchUrisForQuery = (query: string): ((state: State) => Array<string>) =>
|
||||
// replace statement below is kind of ugly, and repeated in doSearch action
|
||||
createSelector(selectSearchResultByQuery, (byQuery) => {
|
||||
if (query) {
|
||||
query = query.replace(/^lbry:\/\//i, '').replace(/\//, ' ');
|
||||
const normalizedQuery = createNormalizedSearchKey(query);
|
||||
return byQuery[normalizedQuery] && byQuery[normalizedQuery]['uris'];
|
||||
}
|
||||
return byQuery[query] && byQuery[query]['uris'];
|
||||
if (!query) return;
|
||||
// replace statement below is kind of ugly, and repeated in doSearch action
|
||||
query = query.replace(/^lbry:\/\//i, '').replace(/\//, ' ');
|
||||
const normalizedQuery = createNormalizedSearchKey(query);
|
||||
return byQuery[normalizedQuery] && byQuery[normalizedQuery]['uris'];
|
||||
});
|
||||
|
||||
export const makeSelectHasReachedMaxResultsLength = (query: string): ((state: State) => boolean) =>
|
||||
|
@ -60,34 +63,91 @@ export const makeSelectHasReachedMaxResultsLength = (query: string): ((state: St
|
|||
|
||||
export const makeSelectRecommendedContentForUri = (uri: string) =>
|
||||
createSelector(
|
||||
makeSelectClaimForUri(uri),
|
||||
selectHistory,
|
||||
selectClaimsByUri,
|
||||
selectShowMatureContent,
|
||||
selectMutedChannels,
|
||||
selectAllCostInfoByUri,
|
||||
selectSearchResultByQuery,
|
||||
makeSelectClaimIsNsfw(uri),
|
||||
(claim, searchUrisByQuery, isMature) => {
|
||||
(history, claimsByUri, matureEnabled, blockedChannels, costInfoByUri, searchUrisByQuery, isMature) => {
|
||||
const claim = claimsByUri[uri];
|
||||
|
||||
if (!claim) return;
|
||||
|
||||
let recommendedContent;
|
||||
if (claim) {
|
||||
// always grab full URL - this can change once search returns canonical
|
||||
const currentUri = buildURI({ streamClaimId: claim.claim_id, streamName: claim.name });
|
||||
// always grab the claimId - this value won't change for filtering
|
||||
const currentClaimId = claim.claim_id;
|
||||
|
||||
const { title } = claim.value;
|
||||
const { title } = claim.value;
|
||||
|
||||
if (!title) {
|
||||
return;
|
||||
}
|
||||
if (!title) return;
|
||||
|
||||
const options: {
|
||||
related_to?: string,
|
||||
nsfw?: boolean,
|
||||
isBackgroundSearch?: boolean,
|
||||
} = { related_to: claim.claim_id, isBackgroundSearch: true };
|
||||
const options: {
|
||||
size: number,
|
||||
nsfw?: boolean,
|
||||
isBackgroundSearch?: boolean,
|
||||
} = { size: 20, nsfw: matureEnabled, isBackgroundSearch: true };
|
||||
|
||||
options['nsfw'] = isMature;
|
||||
const searchQuery = getSearchQueryString(title.replace(/\//, ' '), options);
|
||||
const normalizedSearchQuery = createNormalizedSearchKey(searchQuery);
|
||||
if (SIMPLE_SITE) {
|
||||
options[SEARCH_OPTIONS.CLAIM_TYPE] = SEARCH_OPTIONS.INCLUDE_FILES;
|
||||
options[SEARCH_OPTIONS.MEDIA_VIDEO] = true;
|
||||
options[SEARCH_OPTIONS.PRICE_FILTER_FREE] = true;
|
||||
}
|
||||
if (matureEnabled || (!matureEnabled && !isMature)) {
|
||||
options[SEARCH_OPTIONS.RELATED_TO] = claim.claim_id;
|
||||
}
|
||||
|
||||
let searchResult = searchUrisByQuery[normalizedSearchQuery];
|
||||
if (searchResult) {
|
||||
recommendedContent = searchResult['uris'].filter((searchUri) => searchUri !== currentUri);
|
||||
const searchQuery = getSearchQueryString(title.replace(/\//, ' '), options);
|
||||
const normalizedSearchQuery = createNormalizedSearchKey(searchQuery);
|
||||
|
||||
let searchResult = searchUrisByQuery[normalizedSearchQuery];
|
||||
|
||||
if (searchResult) {
|
||||
// Filter from recommended: The same claim and blocked channels
|
||||
recommendedContent = searchResult['uris'].filter((searchUri) => {
|
||||
const searchClaim = claimsByUri[searchUri];
|
||||
|
||||
if (!searchClaim) return;
|
||||
|
||||
const signingChannel = searchClaim && searchClaim.signing_channel;
|
||||
const channelUri = signingChannel && signingChannel.canonical_url;
|
||||
const blockedMatch = blockedChannels.some((blockedUri) => blockedUri.includes(channelUri));
|
||||
|
||||
let isEqualUri;
|
||||
try {
|
||||
const { claimId: searchId } = parseURI(searchUri);
|
||||
isEqualUri = searchId === currentClaimId;
|
||||
} catch (e) {}
|
||||
|
||||
return !isEqualUri && !blockedMatch;
|
||||
});
|
||||
|
||||
// Claim to play next: playable and free claims not played before in history
|
||||
const nextUriToPlay = recommendedContent.filter((nextRecommendedUri) => {
|
||||
nice nice
|
||||
const costInfo = costInfoByUri[nextRecommendedUri] && costInfoByUri[nextRecommendedUri].cost;
|
||||
const recommendedClaim = claimsByUri[nextRecommendedUri];
|
||||
const isVideo = recommendedClaim && recommendedClaim.value && recommendedClaim.value.stream_type === 'video';
|
||||
const isAudio = recommendedClaim && recommendedClaim.value && recommendedClaim.value.stream_type === 'audio';
|
||||
|
||||
let historyMatch = false;
|
||||
try {
|
||||
const { claimId: nextRecommendedId } = parseURI(nextRecommendedUri);
|
||||
|
||||
historyMatch = history.some(
|
||||
(historyItem) =>
|
||||
(claimsByUri[historyItem.uri] && claimsByUri[historyItem.uri].claim_id) === nextRecommendedId
|
||||
);
|
||||
} catch (e) {}
|
||||
|
||||
return !historyMatch && costInfo === 0 && (isVideo || isAudio);
|
||||
})[0];
|
||||
|
||||
const index = recommendedContent.indexOf(nextUriToPlay);
|
||||
if (index > 0) {
|
||||
const a = recommendedContent[0];
|
||||
recommendedContent[0] = nextUriToPlay;
|
||||
recommendedContent[index] = a;
|
||||
}
|
||||
}
|
||||
return recommendedContent;
|
||||
|
@ -206,6 +266,6 @@ export const makeSelectIsResolvingWinningUri = (query: string = '') => {
|
|||
};
|
||||
|
||||
export const makeSelectUrlForClaimId = (claimId: string) =>
|
||||
createSelector(
|
||||
makeSelectClaimForClaimId(claimId), (claim) => claim ? claim.canonical_url || claim.permanent_url : null
|
||||
createSelector(makeSelectClaimForClaimId(claimId), (claim) =>
|
||||
claim ? claim.canonical_url || claim.permanent_url : null
|
||||
);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// @flow
|
||||
import { SEARCH_OPTIONS } from 'constants/search';
|
||||
import { SIMPLE_SITE } from 'config';
|
||||
|
||||
const DEFAULT_SEARCH_RESULT_FROM = 0;
|
||||
const DEFAULT_SEARCH_SIZE = 20;
|
||||
|
@ -33,7 +32,6 @@ export function updateQueryParam(uri: string, key: string, value: string) {
|
|||
}
|
||||
|
||||
export const getSearchQueryString = (query: string, options: any = {}) => {
|
||||
const FORCE_FREE_ONLY = SIMPLE_SITE;
|
||||
const isSurroundedByQuotes = (str) => str[0] === '"' && str[str.length - 1] === '"';
|
||||
const encodedQuery = encodeURIComponent(query);
|
||||
const queryParams = [
|
||||
|
@ -81,9 +79,11 @@ export const getSearchQueryString = (query: string, options: any = {}) => {
|
|||
const additionalOptions = {};
|
||||
const { related_to } = options;
|
||||
const { nsfw } = options;
|
||||
const { free_only } = options;
|
||||
|
||||
if (related_to) additionalOptions['related_to'] = related_to;
|
||||
if (nsfw === false) additionalOptions['nsfw'] = false;
|
||||
if (related_to) additionalOptions[SEARCH_OPTIONS.RELATED_TO] = related_to;
|
||||
if (free_only) additionalOptions[SEARCH_OPTIONS.PRICE_FILTER_FREE] = true;
|
||||
if (nsfw === false) additionalOptions[SEARCH_OPTIONS.INCLUDE_MATURE] = false;
|
||||
|
||||
if (additionalOptions) {
|
||||
Object.keys(additionalOptions).forEach((key) => {
|
||||
|
@ -92,13 +92,5 @@ export const getSearchQueryString = (query: string, options: any = {}) => {
|
|||
});
|
||||
}
|
||||
|
||||
if (FORCE_FREE_ONLY) {
|
||||
const index = queryParams.findIndex((q) => q.startsWith('free_only'));
|
||||
if (index > -1) {
|
||||
queryParams.splice(index, 1);
|
||||
}
|
||||
queryParams.push(`free_only=true`);
|
||||
}
|
||||
|
||||
return queryParams.join('&');
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue
this is good - did you use it anywhere?