feat: lazy resolve homepage claims

This commit is contained in:
Sean Yesmunt 2019-01-30 02:39:00 -05:00
parent b5d7a2b884
commit 25923505a0
8 changed files with 103 additions and 91 deletions

View file

@ -52,7 +52,7 @@
"hast-util-sanitize": "^1.1.2",
"keytar": "^4.2.1",
"lbry-format": "https://github.com/lbryio/lbry-format.git",
"lbry-redux": "lbryio/lbry-redux#973bbd780a774ba48f4e22861b6accb706cf3835",
"lbry-redux": "lbryio/lbry-redux#aa112ef90e6e5aa37795b349e2bbd6bd6c561d53",
"lbryinc": "lbryio/lbryinc#83c275da7a44f346ce9e796d06f30126f02b4c63",
"localforage": "^1.7.1",
"mammoth": "^1.4.6",

View file

@ -1,20 +1,24 @@
import { connect } from 'react-redux';
import { doFetchClaimsByChannel } from 'redux/actions/content';
import { makeSelectCategoryListUris } from 'redux/selectors/content';
import {
makeSelectClaimsInChannelForCurrentPage,
makeSelectFetchingChannelClaims,
doResolveUris,
selectActiveHistoryEntry,
} from 'lbry-redux';
import { selectShowNsfw } from 'redux/selectors/settings';
import CategoryList from './view';
const select = (state, props) => ({
channelClaims: makeSelectClaimsInChannelForCurrentPage(props.categoryLink)(state),
urisInList: makeSelectCategoryListUris(props.uris, props.categoryLink)(state),
fetching: makeSelectFetchingChannelClaims(props.categoryLink)(state),
obscureNsfw: !selectShowNsfw(state),
currentPageAttributes: selectActiveHistoryEntry(state),
});
const perform = dispatch => ({
fetchChannel: channel => dispatch(doFetchClaimsByChannel(channel)),
resolveUris: uris => dispatch(doResolveUris(uris, true)),
});
export default connect(

View file

@ -1,5 +1,4 @@
// @flow
import type { Claim } from 'types/claim';
import * as ICONS from 'constants/icons';
import React, { PureComponent, createRef } from 'react';
import { normalizeURI } from 'lbry-redux';
@ -11,12 +10,13 @@ import throttle from 'util/throttle';
type Props = {
category: string,
names: ?Array<string>,
categoryLink: ?string,
fetching: boolean,
channelClaims: ?Array<Claim>,
fetchChannel: string => void,
obscureNsfw: boolean,
currentPageAttributes: { scrollY: number },
fetchChannel: string => void,
urisInList: ?Array<string>,
resolveUris: (Array<string>) => void,
};
type State = {
@ -26,9 +26,11 @@ type State = {
class CategoryList extends PureComponent<Props, State> {
static defaultProps = {
categoryLink: '',
categoryLink: undefined,
};
scrollWrapper: { current: null | HTMLUListElement };
constructor() {
super();
@ -40,23 +42,55 @@ class CategoryList extends PureComponent<Props, State> {
(this: any).handleScrollNext = this.handleScrollNext.bind(this);
(this: any).handleScrollPrevious = this.handleScrollPrevious.bind(this);
(this: any).handleArrowButtonsOnScroll = this.handleArrowButtonsOnScroll.bind(this);
(this: any).handleResolveOnScroll = this.handleResolveOnScroll.bind(this);
this.scrollWrapper = createRef();
}
componentDidMount() {
const { fetching, categoryLink, fetchChannel } = this.props;
if (!fetching && categoryLink) {
const { fetching, categoryLink, fetchChannel, resolveUris, urisInList } = this.props;
if (!fetching && categoryLink && (!urisInList || urisInList.length)) {
// Only fetch the channels claims if no urisInList are specifically passed in
// This allows setting a channel link and and passing in a custom list of urisInList (featured content usually works this way)
fetchChannel(categoryLink);
}
const scrollWrapper = this.scrollWrapper.current;
if (scrollWrapper) {
scrollWrapper.addEventListener('scroll', throttle(this.handleArrowButtonsOnScroll, 500));
if (urisInList && window.innerHeight > scrollWrapper.offsetTop) {
resolveUris(urisInList);
}
}
}
scrollWrapper: { current: null | HTMLUListElement };
componentDidUpdate(prevProps: Props) {
const { scrollY: previousScrollY } = prevProps.currentPageAttributes;
const { scrollY } = this.props.currentPageAttributes;
if (scrollY > previousScrollY) {
this.handleResolveOnScroll();
}
}
handleResolveOnScroll() {
const {
urisInList,
resolveUris,
currentPageAttributes: { scrollY },
} = this.props;
const scrollWrapper = this.scrollWrapper.current;
if (!scrollWrapper) {
return;
}
const shouldResolve = window.innerHeight > scrollWrapper.offsetTop - scrollY;
if (shouldResolve && urisInList) {
resolveUris(urisInList);
}
}
handleArrowButtonsOnScroll() {
// Determine if the arrow buttons should be disabled
@ -212,7 +246,7 @@ class CategoryList extends PureComponent<Props, State> {
}
render() {
const { category, categoryLink, names, channelClaims, obscureNsfw } = this.props;
const { category, categoryLink, urisInList, obscureNsfw } = this.props;
const { canScrollNext, canScrollPrevious } = this.state;
const isCommunityTopBids = category.match(/^community/i);
const showScrollButtons = isCommunityTopBids ? !obscureNsfw : true;
@ -273,41 +307,18 @@ class CategoryList extends PureComponent<Props, State> {
</p>
) : (
<ul className="media-scrollhouse" ref={this.scrollWrapper}>
{/*
`names` and `channelClaims` should be combined
it's set up to take a list of names (uris) to show as cards
or a channel link, which it uses for fetch a list of names
having both makes it really confusing
will come back to this once we determine how we will receive channel links
from the homepage uris api call
- sean
*/}
{names &&
!!names.length &&
names.map(name => (
<FileCard showSubscribedLogo key={name} uri={normalizeURI(name)} />
{urisInList &&
urisInList.map(uri => (
<FileCard
placeholder
preventResolve
showSubscribedLogo
key={uri}
uri={normalizeURI(uri)}
/>
))}
{(!names || !names.length) &&
channelClaims &&
channelClaims.length &&
channelClaims
// Only show the first 10 claims, regardless of the amount we have on a channel page
.slice(0, 10)
.map(claim => (
<FileCard
showSubcribedLogo
key={claim.claim_id}
uri={`lbry://${claim.name}#${claim.claim_id}`}
/>
))}
{/*
If there aren't any uris passed in, create an empty array and render placeholder cards
channelClaims or names are being fetched
*/}
{!channelClaims &&
!names &&
{!urisInList &&
/* eslint-disable react/no-array-index-key */
new Array(10).fill(1).map((x, i) => <FileCard placeholder key={i} />)
/* eslint-enable react/no-array-index-key */

View file

@ -29,19 +29,25 @@ type Props = {
isSubscribed: boolean,
isNew: boolean,
placeholder: boolean,
preventResolve: boolean,
};
class FileCard extends React.PureComponent<Props> {
static defaultProps = {
placeholder: false,
preventResolve: false,
};
componentWillMount() {
this.resolve(this.props);
componentDidMount() {
if (!this.props.preventResolve) {
this.resolve(this.props);
}
}
componentWillReceiveProps(nextProps: Props) {
this.resolve(nextProps);
componentDidUpdate() {
if (!this.props.preventResolve) {
this.resolve(this.props);
}
}
resolve = (props: Props) => {
@ -74,7 +80,7 @@ class FileCard extends React.PureComponent<Props> {
return null;
}
if ((!claim && !pending) || placeholder) {
if (!claim && (!pending || placeholder)) {
return (
<li className="media-card media--placeholder">
<div className="media__thumb media__thumb--placeholder" />

View file

@ -9,7 +9,7 @@ const select = state => ({
});
const perform = dispatch => ({
fetchFeaturedUris: () => dispatch(doFetchFeaturedUris()),
fetchFeaturedUris: () => dispatch(doFetchFeaturedUris(true)),
fetchRewardedContent: () => dispatch(doFetchRewardedContent()),
fetchRewards: () => dispatch(doRewardList()),
});

View file

@ -68,7 +68,7 @@ class DiscoverPage extends React.PureComponent<Props> {
<CategoryList
key={category}
category={this.trimClaimIdFromCategory(category)}
names={featuredUris[category]}
uris={featuredUris[category]}
categoryLink={this.getCategoryLinkPartByCategory(category)}
/>
))}

View file

@ -1,8 +1,13 @@
// @flow
import { createSelector } from 'reselect';
import { makeSelectClaimForUri, selectClaimsByUri } from 'lbry-redux';
import {
makeSelectClaimForUri,
selectClaimsByUri,
makeSelectClaimsInChannelForCurrentPage,
} from 'lbry-redux';
import { HISTORY_ITEMS_PER_PAGE } from 'constants/content';
export const selectState = state => state.content || {};
export const selectState = (state: any) => state.content || {};
export const selectPlayingUri = createSelector(selectState, state => state.playingUri);
@ -11,7 +16,7 @@ export const selectChannelClaimCounts = createSelector(
state => state.channelClaimCounts || {}
);
export const makeSelectTotalItemsForChannel = uri =>
export const makeSelectTotalItemsForChannel = (uri: string) =>
createSelector(selectChannelClaimCounts, byUri => byUri && byUri[uri]);
export const selectRewardContentClaimIds = createSelector(
@ -19,7 +24,7 @@ export const selectRewardContentClaimIds = createSelector(
state => state.rewardedContentClaimIds
);
export const makeSelectContentPositionForUri = uri =>
export const makeSelectContentPositionForUri = (uri: string) =>
createSelector(selectState, makeSelectClaimForUri(uri), (state, claim) => {
if (!claim) {
return null;
@ -33,7 +38,7 @@ export const selectHistoryPageCount = createSelector(selectState, state =>
Math.ceil(state.history.length / HISTORY_ITEMS_PER_PAGE)
);
export const makeSelectHistoryForPage = page =>
export const makeSelectHistoryForPage = (page: number) =>
createSelector(selectState, selectClaimsByUri, (state, claimsByUri) => {
const left = page * HISTORY_ITEMS_PER_PAGE;
const historyItems = state.history.slice(left, left + HISTORY_ITEMS_PER_PAGE);
@ -51,5 +56,19 @@ export const makeSelectHistoryForPage = page =>
});
});
export const makeSelectHistoryForUri = uri =>
export const makeSelectHistoryForUri = (uri: string) =>
createSelector(selectState, state => state.history.find(i => i.uri === uri));
export const makeSelectCategoryListUris = (uris: Array<string> = [], channel: string) =>
createSelector(makeSelectClaimsInChannelForCurrentPage(channel), channelClaims => {
if (uris) return uris;
if (channelClaims) {
const CATEGORY_LIST_SIZE = 10;
return channelClaims
.slice(0, CATEGORY_LIST_SIZE)
.map(({ name, claim_id: claimId }) => `${name}#${claimId}`);
}
return null;
});

View file

@ -1793,10 +1793,6 @@ center-align@^0.1.1:
align-text "^0.1.3"
lazy-cache "^1.0.3"
chain-function@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.0.tgz#0d4ab37e7e18ead0bdc47b920764118ce58733dc"
chainsaw@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98"
@ -2659,10 +2655,6 @@ dedent@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
deep-diff@^0.3.5:
version "0.3.8"
resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.8.tgz#c01de63efb0eec9798801d40c7e0dae25b582c84"
deep-equal@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
@ -2859,10 +2851,6 @@ dom-converter@~0.1:
dependencies:
utila "~0.3"
dom-helpers@^3.2.0:
version "3.3.1"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6"
dom-scroll-into-view@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/dom-scroll-into-view/-/dom-scroll-into-view-1.2.1.tgz#e8f36732dd089b0201a88d7815dc3f88e6d66c7e"
@ -5691,9 +5679,9 @@ lbry-redux@lbryio/lbry-redux#84b7d396934d57a37802aadbef71db91230a9404:
reselect "^3.0.0"
uuid "^3.3.2"
lbry-redux@lbryio/lbry-redux#973bbd780a774ba48f4e22861b6accb706cf3835:
lbry-redux@lbryio/lbry-redux#aa112ef90e6e5aa37795b349e2bbd6bd6c561d53:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/973bbd780a774ba48f4e22861b6accb706cf3835"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/aa112ef90e6e5aa37795b349e2bbd6bd6c561d53"
dependencies:
proxy-polyfill "0.1.6"
reselect "^3.0.0"
@ -7694,7 +7682,7 @@ promise@^7.0.3, promise@^7.1.1:
dependencies:
asap "~2.0.3"
prop-types@^15.5.10, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2:
prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2:
version "15.6.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
dependencies:
@ -7891,7 +7879,7 @@ rc@^1.0.1, rc@^1.1.2, rc@^1.1.6, rc@^1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
react-dom@^16.5.0:
react-dom@^16.6.0:
version "16.7.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.7.0.tgz#a17b2a7ca89ee7390bc1ed5eb81783c7461748b8"
dependencies:
@ -7968,16 +7956,6 @@ react-toggle@^4.0.2:
dependencies:
classnames "^2.2.5"
react-transition-group@1.x:
version "1.2.1"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-1.2.1.tgz#e11f72b257f921b213229a774df46612346c7ca6"
dependencies:
chain-function "^1.0.0"
dom-helpers "^3.2.0"
loose-envify "^1.3.1"
prop-types "^15.5.6"
warning "^3.0.0"
react@^0.14.5:
version "0.14.9"
resolved "https://registry.yarnpkg.com/react/-/react-0.14.9.tgz#9110a6497c49d44ba1c0edd317aec29c2e0d91d1"
@ -7985,7 +7963,7 @@ react@^0.14.5:
envify "^3.0.0"
fbjs "^0.6.1"
react@^16.5.0:
react@^16.6.0:
version "16.7.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.7.0.tgz#b674ec396b0a5715873b350446f7ea0802ab6381"
dependencies:
@ -8128,12 +8106,6 @@ reduce-function-call@^1.0.1:
dependencies:
balanced-match "^0.4.2"
redux-logger@^3.0.1:
version "3.0.6"
resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf"
dependencies:
deep-diff "^0.3.5"
redux-persist-transform-compress@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/redux-persist-transform-compress/-/redux-persist-transform-compress-4.2.0.tgz#5089e299df7130878fca45f97ffe82888ba02690"