lbry-desktop/ui/component/claimList/view.jsx
infiinte-persistence 522c6ddcd6 Inf-scroll: Fix "am I at bottom" logic.
If you are at the bottom and you hit the UP_ARROW key one time, the logic still thinks we are at the bottom, despite the component has clearly moved. Similar to the issue noted in the previous commit, this causes the page to increment incorrectly.

My wild guess is that the previous logic depends on the css height, so it might got broken due to css changes. Hopefully the new calculation is more robust and works for all cases.
2020-06-16 16:59:00 -04:00

182 lines
5.7 KiB
JavaScript

// @flow
import { MAIN_WRAPPER_CLASS } from 'component/app/view';
import type { Node } from 'react';
import React, { useEffect, useState } from 'react';
import classnames from 'classnames';
import ClaimPreview from 'component/claimPreview';
import Spinner from 'component/spinner';
import { FormField } from 'component/common/form';
import usePersistedState from 'effects/use-persisted-state';
const SORT_NEW = 'new';
const SORT_OLD = 'old';
type Props = {
uris: Array<string>,
header: Node | boolean,
headerAltControls: Node,
loading: boolean,
type: string,
empty?: string,
defaultSort?: boolean,
onScrollBottom?: any => void,
page?: number,
pageSize?: number,
id?: string,
// If using the default header, this is a unique ID needed to persist the state of the filter setting
persistedStorageKey?: string,
showHiddenByUser: boolean,
showUnresolvedClaims?: boolean,
renderProperties: ?(Claim) => Node,
includeSupportAction?: boolean,
hideBlock: boolean,
injectedItem: ?Node,
timedOutMessage?: Node,
isCardBody?: boolean,
};
export default function ClaimList(props: Props) {
const {
uris,
headerAltControls,
loading,
persistedStorageKey,
empty,
defaultSort,
type,
header,
onScrollBottom,
pageSize,
page,
id,
showHiddenByUser,
showUnresolvedClaims,
renderProperties,
includeSupportAction,
hideBlock,
injectedItem,
timedOutMessage,
isCardBody = false,
} = props;
const [scrollBottomCbMap, setScrollBottomCbMap] = useState({});
const [currentSort, setCurrentSort] = usePersistedState(persistedStorageKey, SORT_NEW);
const timedOut = uris === null;
const urisLength = (uris && uris.length) || 0;
const sortedUris = (urisLength > 0 && (currentSort === SORT_NEW ? uris : uris.slice().reverse())) || [];
function handleSortChange() {
setCurrentSort(currentSort === SORT_NEW ? SORT_OLD : SORT_NEW);
}
useEffect(() => {
setScrollBottomCbMap({});
}, [id, setScrollBottomCbMap]);
useEffect(() => {
let timeout;
function handleScroll(e) {
timeout = setTimeout(() => {
if (page && pageSize && onScrollBottom && !scrollBottomCbMap[page]) {
const mainElWrapper = document.querySelector(`.${MAIN_WRAPPER_CLASS}`);
if (mainElWrapper && !loading && urisLength >= pageSize) {
const contentWrapperAtBottomOfPage =
mainElWrapper.getBoundingClientRect().bottom - 0.5 <= window.innerHeight;
if (contentWrapperAtBottomOfPage) {
onScrollBottom();
// Save that we've fetched this page to avoid weird stuff happening with fast scrolling
setScrollBottomCbMap({ ...scrollBottomCbMap, [page]: true });
}
}
}
}, 150);
}
if (onScrollBottom) {
window.addEventListener('scroll', handleScroll);
return () => {
if (timeout) {
clearTimeout(timeout);
}
window.removeEventListener('scroll', handleScroll);
};
}
}, [loading, onScrollBottom, urisLength, pageSize, page, setScrollBottomCbMap]);
return (
<section
className={classnames('claim-list', {
'claim-list--small': type === 'small',
})}
>
{header !== false && (
<React.Fragment>
{header && (
<div className={classnames('claim-list__header', { 'section__title--small': type === 'small' })}>
{header}
{loading && <Spinner type="small" />}
<div className="claim-list__alt-controls">
{headerAltControls}
{defaultSort && (
<FormField
className="claim-list__dropdown"
type="select"
name="file_sort"
value={currentSort}
onChange={handleSortChange}
>
<option value={SORT_NEW}>{__('Newest First')}</option>
<option value={SORT_OLD}>{__('Oldest First')}</option>
</FormField>
)}
</div>
</div>
)}
</React.Fragment>
)}
{urisLength > 0 && (
<ul
className={classnames('ul--no-style', {
card: !isCardBody,
'claim-list--card-body': isCardBody,
})}
>
{sortedUris.map((uri, index) => (
<React.Fragment key={uri}>
{injectedItem && index === 4 && <li>{injectedItem}</li>}
<ClaimPreview
uri={uri}
type={type}
includeSupportAction={includeSupportAction}
showUnresolvedClaim={showUnresolvedClaims}
properties={renderProperties || (type !== 'small' ? undefined : false)}
showUserBlocked={showHiddenByUser}
hideBlock={hideBlock}
customShouldHide={(claim: StreamClaim) => {
// Hack to hide spee.ch thumbnail publishes
// If it meets these requirements, it was probably uploaded here:
// https://github.com/lbryio/lbry-redux/blob/master/src/redux/actions/publish.js#L74-L79
if (claim.name.length === 24 && !claim.name.includes(' ') && claim.value.author === 'Spee.ch') {
return true;
} else {
return false;
}
}}
/>
</React.Fragment>
))}
</ul>
)}
{!timedOut && urisLength === 0 && !loading && (
<div className="empty empty--centered">{empty || __('No results')}</div>
)}
{timedOut && timedOutMessage && <div className="empty empty--centered">{timedOutMessage}</div>}
</section>
);
}