110 lines
3.3 KiB
TypeScript
110 lines
3.3 KiB
TypeScript
import React, {
|
|
FC, memo, useCallback, useEffect, useState, useRef,
|
|
} from '../../../lib/teact/teact';
|
|
import { ApiWallpaper } from '../../../api/types';
|
|
import { ThemeKey, UPLOADING_WALLPAPER_SLUG } from '../../../types';
|
|
|
|
import { CUSTOM_BG_CACHE_NAME } from '../../../config';
|
|
import * as cacheApi from '../../../util/cacheApi';
|
|
import { fetchBlob } from '../../../util/files';
|
|
import buildClassName from '../../../util/buildClassName';
|
|
import useMedia from '../../../hooks/useMedia';
|
|
import useMediaWithLoadProgress from '../../../hooks/useMediaWithLoadProgress';
|
|
import useShowTransition from '../../../hooks/useShowTransition';
|
|
import usePrevious from '../../../hooks/usePrevious';
|
|
import useCanvasBlur from '../../../hooks/useCanvasBlur';
|
|
|
|
import ProgressSpinner from '../../ui/ProgressSpinner';
|
|
|
|
import './WallpaperTile.scss';
|
|
|
|
type OwnProps = {
|
|
wallpaper: ApiWallpaper;
|
|
theme: ThemeKey;
|
|
isSelected: boolean;
|
|
onClick: (slug: string) => void;
|
|
};
|
|
|
|
const WallpaperTile: FC<OwnProps> = ({
|
|
wallpaper,
|
|
theme,
|
|
isSelected,
|
|
onClick,
|
|
}) => {
|
|
const { slug, document } = wallpaper;
|
|
const localMediaHash = `wallpaper${document.id!}`;
|
|
const localBlobUrl = document.previewBlobUrl;
|
|
const previewBlobUrl = useMedia(`${localMediaHash}?size=m`);
|
|
const thumbRef = useCanvasBlur(document.thumbnail?.dataUri, Boolean(previewBlobUrl), true);
|
|
const { transitionClassNames } = useShowTransition(
|
|
Boolean(previewBlobUrl || localBlobUrl),
|
|
undefined,
|
|
undefined,
|
|
'slow',
|
|
);
|
|
const [isLoadAllowed, setIsLoadAllowed] = useState(false);
|
|
const {
|
|
mediaData: fullMedia, loadProgress,
|
|
} = useMediaWithLoadProgress(localMediaHash, !isLoadAllowed);
|
|
const wasLoadDisabled = usePrevious(isLoadAllowed) === false;
|
|
const { shouldRender: shouldRenderSpinner, transitionClassNames: spinnerClassNames } = useShowTransition(
|
|
(isLoadAllowed && !fullMedia) || slug === UPLOADING_WALLPAPER_SLUG,
|
|
undefined,
|
|
wasLoadDisabled,
|
|
'slow',
|
|
);
|
|
// To prevent triggering of the effect for useCallback
|
|
const cacheKeyRef = useRef<string>();
|
|
cacheKeyRef.current = theme;
|
|
|
|
const handleSelect = useCallback(() => {
|
|
(async () => {
|
|
const blob = await fetchBlob(fullMedia!);
|
|
await cacheApi.save(CUSTOM_BG_CACHE_NAME, cacheKeyRef.current!, blob);
|
|
onClick(slug);
|
|
})();
|
|
}, [fullMedia, onClick, slug]);
|
|
|
|
useEffect(() => {
|
|
if (fullMedia) {
|
|
handleSelect();
|
|
}
|
|
}, [fullMedia, handleSelect]);
|
|
|
|
const handleClick = useCallback(() => {
|
|
if (fullMedia) {
|
|
handleSelect();
|
|
} else {
|
|
setIsLoadAllowed((isAllowed) => !isAllowed);
|
|
}
|
|
}, [fullMedia, handleSelect]);
|
|
|
|
const className = buildClassName(
|
|
'WallpaperTile',
|
|
isSelected && 'selected',
|
|
);
|
|
|
|
return (
|
|
<div className={className} onClick={handleClick}>
|
|
<div className="media-inner">
|
|
<canvas
|
|
ref={thumbRef}
|
|
className="thumbnail"
|
|
/>
|
|
<img
|
|
src={previewBlobUrl || localBlobUrl}
|
|
className={buildClassName('full-media', transitionClassNames)}
|
|
alt=""
|
|
/>
|
|
{shouldRenderSpinner && (
|
|
<div className={buildClassName('spinner-container', spinnerClassNames)}>
|
|
<ProgressSpinner progress={loadProgress} onClick={handleClick} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default memo(WallpaperTile);
|