lbry-desktop/ui/redux/selectors/content.js
jessopb c7021a08ad
Selector refactors (#7424)
* Attempt to speed up sidebar menu for mobile (#283)

* Exclude default homepage data at compile time

The youtuber IDs alone is pretty huge, and is unused in the `CUSTOM_HOMEPAGE=true` configuration.

* Remove Desktop items and other cleanup

- Moved constants out of the component.
- Remove SIMPLE_SITE check.
- Remove Desktop-only items

* Sidebar: limit subscription and tag section

Too slow for huge lists

Limit to 10 initially, and load everything on "Show more"

* Fix makeSelectThumbnailForUri

- Fix memo
- Expose function to extract directly from claim if client already have it.

* Fix and optimize makeSelectIsSubscribed (#273)

- It will not return true if the uri provided is canonical, because the compared subscription uri is in permanent form. This was causing certain elements like the Heart to not appear in claim tiles.
- It is super slow for large subscriptions not just because of the array size + being a hot selector, but also because it is looking up the claim twice (not memo'd) and also calling `parseURI` to determine if it's a channel, which is unnecessary if you already have the claim.

- Optimize the selector to only look up the claim once, and make operations using already-obtained info.

* Simplify makeSelectTitleForUri

No need to memo given no transformation.

* Simplify makeSelectIsUriResolving

- Memo not required. `resolvingUris` is very dynamic and is a short array anyways.
- Changeg from using `indexOf` to `includes`, which is more concise.

* Cost Info selector fixes

- no memo required since they are just directly accessing the store.

Co-authored-by: infinite-persistence <64950861+infinite-persistence@users.noreply.github.com>
Co-authored-by: infinite-persistence <inf.persistence@gmail.com>
2022-01-19 20:46:01 -05:00

172 lines
6.2 KiB
JavaScript

// @flow
import { createSelector } from 'reselect';
import {
makeSelectClaimForUri,
selectClaimsByUri,
selectClaimIsNsfwForUri,
selectClaimIsMineForUri,
makeSelectClaimIsNsfw,
makeSelectContentTypeForUri,
} from 'redux/selectors/claims';
import { makeSelectMediaTypeForUri, makeSelectFileNameForUri } from 'redux/selectors/file_info';
import { selectBalance } from 'redux/selectors/wallet';
import { selectCostInfoForUri } from 'lbryinc';
import { selectShowMatureContent } from 'redux/selectors/settings';
import * as RENDER_MODES from 'constants/file_render_modes';
import path from 'path';
import { FORCE_CONTENT_TYPE_PLAYER, FORCE_CONTENT_TYPE_COMIC } from 'constants/claim';
const RECENT_HISTORY_AMOUNT = 10;
const HISTORY_ITEMS_PER_PAGE = 50;
type State = { claims: any, content: any };
export const selectState = (state: State) => state.content || {};
export const selectPlayingUri = (state: State) => selectState(state).playingUri;
export const selectPrimaryUri = (state: State) => selectState(state).primaryUri;
export const selectListLoop = (state: State) => selectState(state).loopList;
export const selectListShuffle = (state: State) => selectState(state).shuffleList;
export const makeSelectIsPlaying = (uri: string) =>
createSelector(selectPrimaryUri, (primaryUri) => primaryUri === uri);
export const makeSelectIsPlayerFloating = (location: UrlLocation) =>
createSelector(selectPrimaryUri, selectPlayingUri, (primaryUri, playingUri) => {
if (!playingUri) return false;
const { pathname, search } = location;
const hasSecondarySource = Boolean(playingUri.source);
const isComment = playingUri.source === 'comment';
const isInlineSecondaryPlayer =
hasSecondarySource && playingUri.uri !== primaryUri && pathname === playingUri.pathname;
if (isComment && isInlineSecondaryPlayer && search && search !== '?view=discussion') return true;
if (
isInlineSecondaryPlayer ||
(hasSecondarySource && !isComment ? playingUri.primaryUri === primaryUri : playingUri.uri === primaryUri)
) {
return false;
}
return true;
});
export const makeSelectContentPositionForUri = (uri: string) =>
createSelector(selectState, makeSelectClaimForUri(uri), (state, claim) => {
if (!claim) {
return null;
}
const outpoint = `${claim.txid}:${claim.nout}`;
const id = claim.claim_id;
return state.positions[id] ? state.positions[id][outpoint] : null;
});
export const selectHistory = createSelector(selectState, (state) => state.history || []);
export const selectHistoryPageCount = createSelector(selectHistory, (history) =>
Math.ceil(history.length / HISTORY_ITEMS_PER_PAGE)
);
export const makeSelectHistoryForPage = (page: number) =>
createSelector(selectHistory, selectClaimsByUri, (history, claimsByUri) => {
const left = page * HISTORY_ITEMS_PER_PAGE;
const historyItemsForPage = history.slice(left, left + HISTORY_ITEMS_PER_PAGE);
return historyItemsForPage;
});
export const makeSelectHistoryForUri = (uri: string) =>
createSelector(selectHistory, (history) => history.find((i) => i.uri === uri));
export const makeSelectHasVisitedUri = (uri: string) =>
createSelector(makeSelectHistoryForUri(uri), (history) => Boolean(history));
export const selectRecentHistory = createSelector(selectHistory, (history) => {
return history.slice(0, RECENT_HISTORY_AMOUNT);
});
export const selectShouldObscurePreviewForUri = (state: State, uri: string) => {
const showMatureContent = selectShowMatureContent(state);
const isClaimMature = selectClaimIsNsfwForUri(state, uri);
return isClaimMature && !showMatureContent;
};
// deprecated
export const makeSelectShouldObscurePreview = (uri: string) =>
createSelector(selectShowMatureContent, makeSelectClaimIsNsfw(uri), (showMatureContent, isClaimMature) => {
return isClaimMature && !showMatureContent;
});
// should probably be in lbry-redux, yarn link was fighting me
export const makeSelectFileExtensionForUri = (uri: string) =>
createSelector(makeSelectFileNameForUri(uri), (fileName) => {
return fileName && path.extname(fileName).substring(1);
});
export const makeSelectFileRenderModeForUri = (uri: string) =>
createSelector(
makeSelectContentTypeForUri(uri),
makeSelectMediaTypeForUri(uri),
makeSelectFileExtensionForUri(uri),
(contentType, mediaType, extension) => {
if (mediaType === 'video' || FORCE_CONTENT_TYPE_PLAYER.includes(contentType)) {
return RENDER_MODES.VIDEO;
}
if (mediaType === 'audio') {
return RENDER_MODES.AUDIO;
}
if (mediaType === 'image') {
return RENDER_MODES.IMAGE;
}
if (['md', 'markdown'].includes(extension) || ['text/md', 'text/markdown'].includes(contentType)) {
return RENDER_MODES.MARKDOWN;
}
if (contentType === 'application/pdf') {
return RENDER_MODES.PDF;
}
if (['text/htm', 'text/html'].includes(contentType)) {
return RENDER_MODES.HTML;
}
if (['text', 'document', 'script'].includes(mediaType)) {
return RENDER_MODES.DOCUMENT;
}
if (extension === 'docx') {
return RENDER_MODES.DOCX;
}
// when writing this my local copy of Lbry.getMediaType had '3D-file', but I was receiving model...'
if (['3D-file', 'model'].includes(mediaType)) {
return RENDER_MODES.CAD;
}
// Force content type for fallback support of older claims
if (mediaType === 'comic-book' || FORCE_CONTENT_TYPE_COMIC.includes(contentType)) {
return RENDER_MODES.COMIC;
}
if (
[
'application/zip',
'application/x-gzip',
'application/x-gtar',
'application/x-tgz',
'application/vnd.rar',
'application/x-7z-compressed',
].includes(contentType)
) {
return RENDER_MODES.DOWNLOAD;
}
if (mediaType === 'application') {
return RENDER_MODES.APPLICATION;
}
return RENDER_MODES.UNSUPPORTED;
}
);
export const selectInsufficientCreditsForUri = (state: State, uri: string) => {
const isMine = selectClaimIsMineForUri(state, uri);
const costInfo = selectCostInfoForUri(state, uri);
const balance = selectBalance(state);
return !isMine && costInfo && costInfo.cost > 0 && costInfo.cost > balance;
};