Fix #5795 - Claim images not lazy loaded

This commit is contained in:
Louis Sandoval 2021-04-06 22:33:36 -07:00 committed by Sean Yesmunt
parent fe69ef2c90
commit a7cb0e240e
5 changed files with 69 additions and 5 deletions

View file

@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Fixed
- Lazy-load claim images to improve app responsiveness ([#5795](https://github.com/lbryio/lbry-desktop/issues/5795))
- Fix display of upload date and view count on smaller screens ([#5822](https://github.com/lbryio/lbry-desktop/issues/5822))
- Autoplay looping to a previous video or itself ([#5711](https://github.com/lbryio/lbry-desktop/pull/5711))
- Autoplay not working in mini-player mode ([#5716](https://github.com/lbryio/lbry-desktop/pull/5716))

View file

@ -5,6 +5,7 @@ import classnames from 'classnames';
import Gerbil from './gerbil.png';
import FreezeframeWrapper from 'component/fileThumbnail/FreezeframeWrapper';
import ChannelStakedIndicator from 'component/channelStakedIndicator';
import useLazyLoading from '../../util/useLazyLoading';
type Props = {
thumbnail: ?string,
@ -42,6 +43,7 @@ function ChannelThumbnail(props: Props) {
const thumbnailPreview = rawThumbnailPreview && rawThumbnailPreview.trim().replace(/^http:\/\//i, 'https://');
const channelThumbnail = thumbnail || thumbnailPreview;
const showThumb = (!obscure && !!thumbnail) || thumbnailPreview;
const thumbnailRef = React.useRef(null);
// Generate a random color class based on the first letter of the channel name
const { channelName } = parseURI(uri);
let initializer;
@ -59,6 +61,8 @@ function ChannelThumbnail(props: Props) {
}
}, [doResolveUri, shouldResolve, uri]);
useLazyLoading(thumbnailRef, 0.25, [showThumb]);
if (channelThumbnail && channelThumbnail.endsWith('gif') && !allowGifs) {
return (
<FreezeframeWrapper src={channelThumbnail} className={classnames('channel-thumbnail', className)}>
@ -77,9 +81,10 @@ function ChannelThumbnail(props: Props) {
>
{!showThumb && (
<img
ref={thumbnailRef}
alt={__('Channel profile picture')}
className="channel-thumbnail__default"
src={!thumbError && thumbnailPreview ? thumbnailPreview : Gerbil}
data-src={!thumbError && thumbnailPreview ? thumbnailPreview : Gerbil}
onError={() => setThumbError(true)} // if thumb fails (including due to https replace, show gerbil.
/>
)}
@ -89,9 +94,10 @@ function ChannelThumbnail(props: Props) {
<div className="chanel-thumbnail--waiting">{__('This will be visible in a few minutes.')}</div>
) : (
<img
ref={thumbnailRef}
alt={__('Channel profile picture')}
className="channel-thumbnail__custom"
src={!thumbError ? thumbnailPreview || thumbnail : Gerbil}
data-src={!thumbError ? thumbnailPreview || thumbnail : Gerbil}
onError={() => setThumbError(true)} // if thumb fails (including due to https replace, show gerbil.
/>
)}

View file

@ -2,6 +2,7 @@ import React, { useEffect } from 'react';
import classnames from 'classnames';
import PropTypes from 'prop-types';
import Freezeframe from './FreezeframeLite';
import useLazyLoading from '../../util/useLazyLoading';
const FreezeframeWrapper = (props) => {
const imgRef = React.useRef();
@ -13,10 +14,12 @@ const FreezeframeWrapper = (props) => {
freezeframe.current = new Freezeframe(imgRef.current);
}, []);
useLazyLoading(imgRef);
return (
<div className={classnames(className, 'freezeframe-wrapper')}>
<>
<img ref={imgRef} src={src} className="freezeframe-img" />
<img ref={imgRef} data-src={src} className="freezeframe-img" />
{children}
</>
</div>

View file

@ -5,6 +5,7 @@ import React from 'react';
import FreezeframeWrapper from './FreezeframeWrapper';
import Placeholder from './placeholder.png';
import classnames from 'classnames';
import useLazyLoading from '../../util/useLazyLoading';
type Props = {
uri: string,
@ -12,7 +13,7 @@ type Props = {
children?: Node,
allowGifs: boolean,
claim: ?StreamClaim,
doResolveUri: string => void,
doResolveUri: (string) => void,
className?: string,
};
@ -23,6 +24,7 @@ function FileThumbnail(props: Props) {
uri && claim && claim.value && claim.value.thumbnail ? claim.value.thumbnail.url : undefined;
const thumbnail = passedThumbnail || thumbnailFromClaim;
const hasResolvedClaim = claim !== undefined;
const thumbnailRef = React.useRef(null);
React.useEffect(() => {
if (!hasResolvedClaim && uri) {
@ -30,6 +32,8 @@ function FileThumbnail(props: Props) {
}
}, [hasResolvedClaim, uri, doResolveUri]);
useLazyLoading(thumbnailRef);
if (!allowGifs && thumbnail && thumbnail.endsWith('gif')) {
return (
<FreezeframeWrapper src={thumbnail} className={classnames('media__thumb', className)}>
@ -46,9 +50,12 @@ function FileThumbnail(props: Props) {
}
// @endif
const thumnailUrl = url ? url.replace(/'/g, "\\'") : '';
return (
<div
style={{ backgroundImage: `url('${url ? url.replace(/'/g, "\\'") : ''}')` }}
ref={thumbnailRef}
data-background-image={thumnailUrl}
className={classnames('media__thumb', className, {
'media__thumb--resolving': !hasResolvedClaim,
})}

47
ui/util/useLazyLoading.js Normal file
View file

@ -0,0 +1,47 @@
import { useEffect } from 'react';
/**
* Helper React hook for lazy loading images
* @param elementRef - A React useRef instance to the element to lazy load.
* @param {Number} [threshold=0.5] - The percent visible in order for loading to begin.
* @param {Array<>} [deps=[]] - The dependencies this lazy-load is reliant on.
*/
export default function useLazyLoading(elementRef, threshold = 0.25, deps = []) {
useEffect(() => {
if (!elementRef.current) {
return;
}
const lazyLoadingObserver = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.intersectionRatio >= threshold) {
const { target } = entry;
observer.unobserve(target);
// useful for lazy loading img tags
if (target.dataset.src) {
target.src = target.dataset.src;
return;
}
// useful for lazy loading background images on divs
if (target.dataset.backgroundImage) {
target.style.backgroundImage = `url(${target.dataset.backgroundImage})`;
}
}
});
},
{
root: null,
rootMargin: '0px',
threshold,
}
);
lazyLoadingObserver.observe(elementRef.current);
// re-run whenever the element ref changes
}, deps);
}