Fix #5795 - Claim images not lazy loaded
This commit is contained in:
parent
fe69ef2c90
commit
a7cb0e240e
5 changed files with 69 additions and 5 deletions
|
@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
### Fixed
|
### 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))
|
- 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 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))
|
- Autoplay not working in mini-player mode ([#5716](https://github.com/lbryio/lbry-desktop/pull/5716))
|
||||||
|
|
|
@ -5,6 +5,7 @@ import classnames from 'classnames';
|
||||||
import Gerbil from './gerbil.png';
|
import Gerbil from './gerbil.png';
|
||||||
import FreezeframeWrapper from 'component/fileThumbnail/FreezeframeWrapper';
|
import FreezeframeWrapper from 'component/fileThumbnail/FreezeframeWrapper';
|
||||||
import ChannelStakedIndicator from 'component/channelStakedIndicator';
|
import ChannelStakedIndicator from 'component/channelStakedIndicator';
|
||||||
|
import useLazyLoading from '../../util/useLazyLoading';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
thumbnail: ?string,
|
thumbnail: ?string,
|
||||||
|
@ -42,6 +43,7 @@ function ChannelThumbnail(props: Props) {
|
||||||
const thumbnailPreview = rawThumbnailPreview && rawThumbnailPreview.trim().replace(/^http:\/\//i, 'https://');
|
const thumbnailPreview = rawThumbnailPreview && rawThumbnailPreview.trim().replace(/^http:\/\//i, 'https://');
|
||||||
const channelThumbnail = thumbnail || thumbnailPreview;
|
const channelThumbnail = thumbnail || thumbnailPreview;
|
||||||
const showThumb = (!obscure && !!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
|
// Generate a random color class based on the first letter of the channel name
|
||||||
const { channelName } = parseURI(uri);
|
const { channelName } = parseURI(uri);
|
||||||
let initializer;
|
let initializer;
|
||||||
|
@ -59,6 +61,8 @@ function ChannelThumbnail(props: Props) {
|
||||||
}
|
}
|
||||||
}, [doResolveUri, shouldResolve, uri]);
|
}, [doResolveUri, shouldResolve, uri]);
|
||||||
|
|
||||||
|
useLazyLoading(thumbnailRef, 0.25, [showThumb]);
|
||||||
|
|
||||||
if (channelThumbnail && channelThumbnail.endsWith('gif') && !allowGifs) {
|
if (channelThumbnail && channelThumbnail.endsWith('gif') && !allowGifs) {
|
||||||
return (
|
return (
|
||||||
<FreezeframeWrapper src={channelThumbnail} className={classnames('channel-thumbnail', className)}>
|
<FreezeframeWrapper src={channelThumbnail} className={classnames('channel-thumbnail', className)}>
|
||||||
|
@ -77,9 +81,10 @@ function ChannelThumbnail(props: Props) {
|
||||||
>
|
>
|
||||||
{!showThumb && (
|
{!showThumb && (
|
||||||
<img
|
<img
|
||||||
|
ref={thumbnailRef}
|
||||||
alt={__('Channel profile picture')}
|
alt={__('Channel profile picture')}
|
||||||
className="channel-thumbnail__default"
|
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.
|
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>
|
<div className="chanel-thumbnail--waiting">{__('This will be visible in a few minutes.')}</div>
|
||||||
) : (
|
) : (
|
||||||
<img
|
<img
|
||||||
|
ref={thumbnailRef}
|
||||||
alt={__('Channel profile picture')}
|
alt={__('Channel profile picture')}
|
||||||
className="channel-thumbnail__custom"
|
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.
|
onError={() => setThumbError(true)} // if thumb fails (including due to https replace, show gerbil.
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import React, { useEffect } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Freezeframe from './FreezeframeLite';
|
import Freezeframe from './FreezeframeLite';
|
||||||
|
import useLazyLoading from '../../util/useLazyLoading';
|
||||||
|
|
||||||
const FreezeframeWrapper = (props) => {
|
const FreezeframeWrapper = (props) => {
|
||||||
const imgRef = React.useRef();
|
const imgRef = React.useRef();
|
||||||
|
@ -13,10 +14,12 @@ const FreezeframeWrapper = (props) => {
|
||||||
freezeframe.current = new Freezeframe(imgRef.current);
|
freezeframe.current = new Freezeframe(imgRef.current);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useLazyLoading(imgRef);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classnames(className, 'freezeframe-wrapper')}>
|
<div className={classnames(className, 'freezeframe-wrapper')}>
|
||||||
<>
|
<>
|
||||||
<img ref={imgRef} src={src} className="freezeframe-img" />
|
<img ref={imgRef} data-src={src} className="freezeframe-img" />
|
||||||
{children}
|
{children}
|
||||||
</>
|
</>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import React from 'react';
|
||||||
import FreezeframeWrapper from './FreezeframeWrapper';
|
import FreezeframeWrapper from './FreezeframeWrapper';
|
||||||
import Placeholder from './placeholder.png';
|
import Placeholder from './placeholder.png';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import useLazyLoading from '../../util/useLazyLoading';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
uri: string,
|
uri: string,
|
||||||
|
@ -12,7 +13,7 @@ type Props = {
|
||||||
children?: Node,
|
children?: Node,
|
||||||
allowGifs: boolean,
|
allowGifs: boolean,
|
||||||
claim: ?StreamClaim,
|
claim: ?StreamClaim,
|
||||||
doResolveUri: string => void,
|
doResolveUri: (string) => void,
|
||||||
className?: string,
|
className?: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -23,6 +24,7 @@ function FileThumbnail(props: Props) {
|
||||||
uri && claim && claim.value && claim.value.thumbnail ? claim.value.thumbnail.url : undefined;
|
uri && claim && claim.value && claim.value.thumbnail ? claim.value.thumbnail.url : undefined;
|
||||||
const thumbnail = passedThumbnail || thumbnailFromClaim;
|
const thumbnail = passedThumbnail || thumbnailFromClaim;
|
||||||
const hasResolvedClaim = claim !== undefined;
|
const hasResolvedClaim = claim !== undefined;
|
||||||
|
const thumbnailRef = React.useRef(null);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!hasResolvedClaim && uri) {
|
if (!hasResolvedClaim && uri) {
|
||||||
|
@ -30,6 +32,8 @@ function FileThumbnail(props: Props) {
|
||||||
}
|
}
|
||||||
}, [hasResolvedClaim, uri, doResolveUri]);
|
}, [hasResolvedClaim, uri, doResolveUri]);
|
||||||
|
|
||||||
|
useLazyLoading(thumbnailRef);
|
||||||
|
|
||||||
if (!allowGifs && thumbnail && thumbnail.endsWith('gif')) {
|
if (!allowGifs && thumbnail && thumbnail.endsWith('gif')) {
|
||||||
return (
|
return (
|
||||||
<FreezeframeWrapper src={thumbnail} className={classnames('media__thumb', className)}>
|
<FreezeframeWrapper src={thumbnail} className={classnames('media__thumb', className)}>
|
||||||
|
@ -46,9 +50,12 @@ function FileThumbnail(props: Props) {
|
||||||
}
|
}
|
||||||
// @endif
|
// @endif
|
||||||
|
|
||||||
|
const thumnailUrl = url ? url.replace(/'/g, "\\'") : '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{ backgroundImage: `url('${url ? url.replace(/'/g, "\\'") : ''}')` }}
|
ref={thumbnailRef}
|
||||||
|
data-background-image={thumnailUrl}
|
||||||
className={classnames('media__thumb', className, {
|
className={classnames('media__thumb', className, {
|
||||||
'media__thumb--resolving': !hasResolvedClaim,
|
'media__thumb--resolving': !hasResolvedClaim,
|
||||||
})}
|
})}
|
||||||
|
|
47
ui/util/useLazyLoading.js
Normal file
47
ui/util/useLazyLoading.js
Normal 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);
|
||||||
|
}
|
Loading…
Reference in a new issue