import { forwardRef, useCallback, useMemo, useRef, useState } from 'react';
import { motion } from 'framer-motion';
import PropTypes from 'prop-types';
import { clsm, noop } from '../../../../utils';
import { createAnimationProps } from '../../../../helpers/animationPropsHelper';
import { dashboard as $content } from '../../../../content';
import { Delete, Upload } from '../../../../assets/icons';
import {
MAXIMUM_IMAGE_FILE_SIZE,
SUPPORTED_IMAGE_FILE_FORMATS
} from '../../../../constants';
import { useModal } from '../../../../contexts/Modal';
import Button from '../../../../components/Button';
import NoImageSrcIcon from '../../../../components/IconSelect/NoImageSrcIcon';
import Spinner from '../../../../components/Spinner';
import useImageUpload from '../../../../hooks/useImageUpload';
import useResize from '../../../../hooks/useResize';
const $channelAssetModalContent = $content.modal.channel_asset_deletion_modal;
const STACKING_BREAKPOINT = 910; // px
const isStackingBreakpoint = () => window.innerWidth < STACKING_BREAKPOINT;
const ImageUploader = forwardRef(
(
{
assetType,
className,
onDelete,
onImageDownload,
onUpload,
previewShape,
shouldAnimate,
uploadUrl
},
containerRef
) => {
const maximumFileSize = MAXIMUM_IMAGE_FILE_SIZE[assetType];
const {
deleteChannelAsset,
isDeleting,
isDownloading,
isUploading,
setIsDownloading,
uploadChannelAsset
} = useImageUpload({ assetType, onDelete, onUpload, maximumFileSize });
const { openModal } = useModal();
const [shouldStack, setShouldStack] = useState(isStackingBreakpoint());
const fileInputRef = useRef();
const deleteButtonRef = useRef();
const hasUploadUrl = !!uploadUrl;
const isLoadingNewImage = isUploading || isDownloading;
const acceptedImageFileFormats = SUPPORTED_IMAGE_FILE_FORMATS.flat()
.map((fileFormat) => `.${fileFormat}`)
.join(',');
const previewClasses = useMemo(() => {
const classes = [
'bg-lightMode-gray-extraLight',
'dark:bg-darkMode-gray',
'w-auto',
shouldStack ? 'h-full' : `h-[124px]`
];
if (previewShape === 'round')
return classes.concat(['aspect-square', 'rounded-full']);
if (previewShape === '16/9')
return classes.concat(['aspect-video', 'rounded-xl']);
}, [previewShape, shouldStack]);
useResize(useCallback(() => setShouldStack(isStackingBreakpoint()), []));
const handleUpload = async (e) => {
await uploadChannelAsset(e);
e.target.value = '';
};
const handleDelete = () => {
const confirmText = $channelAssetModalContent.delete_image;
const message = $channelAssetModalContent.confirm_intent_message.replace(
'{ASSET_TYPE}',
assetType
);
openModal({
content: { confirmText, message, isDestructive: true },
onConfirm: deleteChannelAsset,
lastFocusedElement: deleteButtonRef
});
};
const handleImageFullyDownloaded = useCallback(() => {
// This condition prevents `onImageDownload` to be called on the initial render
if (isDownloading) {
onImageDownload();
}
setIsDownloading(false);
}, [isDownloading, onImageDownload, setIsDownloading]);
const renderPreview = useCallback(() => {
if (isUploading || hasUploadUrl) {
return (
<>
{/* If the image is done being uploaded we want to also wait for the the image
to completely download before replacing the spinner with the image tag. */}
{hasUploadUrl && (
)}
{isLoadingNewImage && (
{$content.settings_page.upload_restrictions .replace( '{FILE_FORMATS}', SUPPORTED_IMAGE_FILE_FORMATS.map((fileFormat) => (typeof fileFormat === 'string' ? fileFormat : fileFormat[0] ).toUpperCase() ).join(', ') ) .replace('{MAXIMUM_FILE_SIZE}', maximumFileSize)}