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
|
||||
|
||||
- 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))
|
||||
|
|
|
@ -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.
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
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