049fb2878e
* recsys wip better logging fix floating player popout playing uri bug with recsys lint add empty entries to create use beacon; fire on visibilitychange cleanup, not record recs if not seen ifweb recsys beacon recsys handle embeds, cleanup use history.listen to trigger events fix recsys embed bug bugfix more default data cleaner cleaner * remove tentative * disable recsys debug logging
211 lines
7.1 KiB
JavaScript
211 lines
7.1 KiB
JavaScript
// @flow
|
|
import { getSearchQueryString } from 'util/query-params';
|
|
import { selectShowMatureContent } from 'redux/selectors/settings';
|
|
import {
|
|
parseURI,
|
|
makeSelectClaimForUri,
|
|
makeSelectClaimForClaimId,
|
|
makeSelectClaimIsNsfw,
|
|
buildURI,
|
|
isClaimNsfw,
|
|
makeSelectPendingClaimForUri,
|
|
makeSelectIsUriResolving,
|
|
} from 'lbry-redux';
|
|
import { createSelector } from 'reselect';
|
|
import { createNormalizedSearchKey } from 'util/search';
|
|
|
|
type State = { search: SearchState };
|
|
|
|
export const selectState = (state: State): SearchState => state.search;
|
|
|
|
export const selectSearchValue: (state: State) => string = createSelector(selectState, (state) => state.searchQuery);
|
|
|
|
export const selectSearchOptions: (state: State) => SearchOptions = createSelector(
|
|
selectState,
|
|
(state) => state.options
|
|
);
|
|
|
|
export const selectIsSearching: (state: State) => boolean = createSelector(selectState, (state) => state.searching);
|
|
|
|
export const selectSearchResultByQuery: (state: State) => { [string]: Array<string> } = createSelector(
|
|
selectState,
|
|
(state) => state.resultsByQuery
|
|
);
|
|
|
|
export const selectHasReachedMaxResultsLength: (state: State) => { [boolean]: Array<boolean> } = createSelector(
|
|
selectState,
|
|
(state) => state.hasReachedMaxResultsLength
|
|
);
|
|
|
|
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'];
|
|
});
|
|
|
|
export const makeSelectHasReachedMaxResultsLength = (query: string): ((state: State) => boolean) =>
|
|
createSelector(selectHasReachedMaxResultsLength, (hasReachedMaxResultsLength) => {
|
|
if (query) {
|
|
query = query.replace(/^lbry:\/\//i, '').replace(/\//, ' ');
|
|
const normalizedQuery = createNormalizedSearchKey(query);
|
|
return hasReachedMaxResultsLength[normalizedQuery];
|
|
}
|
|
return hasReachedMaxResultsLength[query];
|
|
});
|
|
|
|
export const makeSelectRecommendedContentForUri = (uri: string) =>
|
|
createSelector(
|
|
makeSelectClaimForUri(uri),
|
|
selectSearchResultByQuery,
|
|
makeSelectClaimIsNsfw(uri),
|
|
(claim, searchUrisByQuery, isMature) => {
|
|
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 });
|
|
|
|
const { title } = claim.value;
|
|
|
|
if (!title) {
|
|
return;
|
|
}
|
|
|
|
const options: {
|
|
related_to?: string,
|
|
nsfw?: boolean,
|
|
isBackgroundSearch?: boolean,
|
|
} = { related_to: claim.claim_id, isBackgroundSearch: true };
|
|
|
|
options['nsfw'] = isMature;
|
|
const searchQuery = getSearchQueryString(title.replace(/\//, ' '), options);
|
|
const normalizedSearchQuery = createNormalizedSearchKey(searchQuery);
|
|
|
|
let searchResult = searchUrisByQuery[normalizedSearchQuery];
|
|
if (searchResult) {
|
|
recommendedContent = searchResult['uris'].filter((searchUri) => searchUri !== currentUri);
|
|
}
|
|
}
|
|
return recommendedContent;
|
|
}
|
|
);
|
|
|
|
export const makeSelectRecommendedRecsysIdForClaimId = (claimId: string) =>
|
|
createSelector(makeSelectClaimForClaimId(claimId), selectSearchResultByQuery, (claim, searchUrisByQuery) => {
|
|
// TODO: DRY this out.
|
|
let poweredBy;
|
|
if (claim && claimId) {
|
|
const isMature = isClaimNsfw(claim);
|
|
const { title } = claim.value;
|
|
|
|
if (!title) {
|
|
return;
|
|
}
|
|
|
|
const options: {
|
|
related_to?: string,
|
|
nsfw?: boolean,
|
|
isBackgroundSearch?: boolean,
|
|
} = { related_to: claim.claim_id, isBackgroundSearch: true };
|
|
|
|
options['nsfw'] = isMature;
|
|
const searchQuery = getSearchQueryString(title.replace(/\//, ' '), options);
|
|
const normalizedSearchQuery = createNormalizedSearchKey(searchQuery);
|
|
|
|
let searchResult = searchUrisByQuery[normalizedSearchQuery];
|
|
if (searchResult) {
|
|
poweredBy = searchResult.recsys;
|
|
} else {
|
|
return normalizedSearchQuery;
|
|
}
|
|
}
|
|
return poweredBy;
|
|
});
|
|
|
|
export const makeSelectWinningUriForQuery = (query: string) => {
|
|
const uriFromQuery = `lbry://${query}`;
|
|
|
|
let channelUriFromQuery;
|
|
try {
|
|
const { isChannel } = parseURI(uriFromQuery);
|
|
if (!isChannel) {
|
|
channelUriFromQuery = `lbry://@${query}`;
|
|
}
|
|
} catch (e) {}
|
|
|
|
return createSelector(
|
|
selectShowMatureContent,
|
|
makeSelectPendingClaimForUri(uriFromQuery),
|
|
makeSelectClaimForUri(uriFromQuery),
|
|
makeSelectClaimForUri(channelUriFromQuery),
|
|
(matureEnabled, pendingClaim, claim1, claim2) => {
|
|
const claim1Mature = claim1 && isClaimNsfw(claim1);
|
|
const claim2Mature = claim2 && isClaimNsfw(claim2);
|
|
let pendingAmount = pendingClaim && pendingClaim.amount;
|
|
|
|
if (!claim1 && !claim2) {
|
|
return undefined;
|
|
} else if (!claim1 && claim2) {
|
|
return matureEnabled ? claim2.canonical_url : claim2Mature ? undefined : claim2.canonical_url;
|
|
} else if (claim1 && !claim2) {
|
|
return matureEnabled
|
|
? claim1.repost_url || claim1.canonical_url
|
|
: claim1Mature
|
|
? undefined
|
|
: claim1.repost_url || claim1.canonical_url;
|
|
}
|
|
|
|
const effectiveAmount1 = claim1 && (claim1.repost_bid_amount || claim1.meta.effective_amount);
|
|
// claim2 will never have a repost_bid_amount because reposts never start with "@"
|
|
const effectiveAmount2 = claim2 && claim2.meta.effective_amount;
|
|
|
|
if (!matureEnabled) {
|
|
if (claim1Mature && !claim2Mature) {
|
|
return claim2.canonical_url;
|
|
} else if (claim2Mature && !claim1Mature) {
|
|
return claim1.repost_url || claim1.canonical_url;
|
|
} else if (claim1Mature && claim2Mature) {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
const returnBeforePending =
|
|
Number(effectiveAmount1) > Number(effectiveAmount2)
|
|
? claim1.repost_url || claim1.canonical_url
|
|
: claim2.canonical_url;
|
|
if (pendingAmount && pendingAmount > effectiveAmount1 && pendingAmount > effectiveAmount2) {
|
|
return pendingAmount.permanent_url;
|
|
} else {
|
|
return returnBeforePending;
|
|
}
|
|
}
|
|
);
|
|
};
|
|
|
|
export const makeSelectIsResolvingWinningUri = (query: string = '') => {
|
|
const uriFromQuery = `lbry://${query}`;
|
|
let channelUriFromQuery;
|
|
try {
|
|
const { isChannel } = parseURI(uriFromQuery);
|
|
if (!isChannel) {
|
|
channelUriFromQuery = `lbry://@${query}`;
|
|
}
|
|
} catch (e) {}
|
|
|
|
return createSelector(
|
|
makeSelectIsUriResolving(uriFromQuery),
|
|
channelUriFromQuery ? makeSelectIsUriResolving(channelUriFromQuery) : () => {},
|
|
(claim1IsResolving, claim2IsResolving) => {
|
|
return claim1IsResolving || claim2IsResolving;
|
|
}
|
|
);
|
|
};
|
|
|
|
export const makeSelectUrlForClaimId = (claimId: string) =>
|
|
createSelector(
|
|
makeSelectClaimForClaimId(claimId), (claim) => claim ? claim.canonical_url || claim.permanent_url : null
|
|
);
|