Refine file download behavior
This commit is contained in:
parent
0dc627b774
commit
d3c8db474e
@ -4,7 +4,11 @@ import { ComponentProps, useMemo } from 'react';
|
|||||||
import { pathForAdminPhotoEdit, pathForPhoto } from '@/site/paths';
|
import { pathForAdminPhotoEdit, pathForPhoto } from '@/site/paths';
|
||||||
import { deletePhotoAction, toggleFavoritePhotoAction } from '@/photo/actions';
|
import { deletePhotoAction, toggleFavoritePhotoAction } from '@/photo/actions';
|
||||||
import { FaRegEdit, FaRegStar, FaStar } from 'react-icons/fa';
|
import { FaRegEdit, FaRegStar, FaStar } from 'react-icons/fa';
|
||||||
import { Photo, deleteConfirmationTextForPhoto } from '@/photo';
|
import {
|
||||||
|
Photo,
|
||||||
|
deleteConfirmationTextForPhoto,
|
||||||
|
downloadFileNameForPhoto,
|
||||||
|
} from '@/photo';
|
||||||
import { isPathFavs, isPhotoFav } from '@/tag';
|
import { isPathFavs, isPhotoFav } from '@/tag';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
import { BiTrash } from 'react-icons/bi';
|
import { BiTrash } from 'react-icons/bi';
|
||||||
@ -64,7 +68,7 @@ export default function AdminPhotoMenuClient({
|
|||||||
className="translate-x-[-1.5px] translate-y-[-0.5px]"
|
className="translate-x-[-1.5px] translate-y-[-0.5px]"
|
||||||
/>,
|
/>,
|
||||||
href: photo.url,
|
href: photo.url,
|
||||||
hrefDownloadName: photo.url.split('/').pop(),
|
hrefDownloadName: downloadFileNameForPhoto(photo),
|
||||||
});
|
});
|
||||||
items.push({
|
items.push({
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { MdOutlineFileDownload } from 'react-icons/md';
|
import { MdOutlineFileDownload } from 'react-icons/md';
|
||||||
import PathLoaderButton from './primitives/PathLoaderButton';
|
|
||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
import { Photo } from '@/photo';
|
import { downloadFileNameForPhoto, Photo } from '@/photo';
|
||||||
|
import LoaderButton from './primitives/LoaderButton';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
export default function DownloadButton({
|
export default function DownloadButton({
|
||||||
photo,
|
photo,
|
||||||
@ -12,30 +13,30 @@ export default function DownloadButton({
|
|||||||
dim?: boolean
|
dim?: boolean
|
||||||
className?: string
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
const {url, title} = photo;
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PathLoaderButton
|
<LoaderButton
|
||||||
path={url}
|
title="Download Original File"
|
||||||
className={clsx(
|
className={clsx(
|
||||||
className,
|
className,
|
||||||
dim ? 'text-dim' : 'text-medium',
|
dim ? 'text-dim' : 'text-medium',
|
||||||
'-mx-0.5 translate-x-0.5',
|
'-mx-0.5 translate-x-0.5',
|
||||||
'sm:mx-0 sm:translate-x-0'
|
'sm:mx-0 sm:translate-x-0'
|
||||||
)}
|
)}
|
||||||
icon={<MdOutlineFileDownload size={16} />}
|
icon={<MdOutlineFileDownload size={18} />}
|
||||||
spinnerColor='dim'
|
spinnerColor='dim'
|
||||||
styleAs='link'
|
styleAs='link'
|
||||||
shouldReplace
|
isLoading={isLoading}
|
||||||
handleAction={async () => {
|
onClick={async () => {
|
||||||
const response = await fetch(url);
|
setIsLoading(true);
|
||||||
const blob = await response.blob();
|
const blob = await fetch(photo.url)
|
||||||
|
.then(response => response.blob())
|
||||||
|
.finally(() => setIsLoading(false));
|
||||||
const downloadUrl = window.URL.createObjectURL(blob);
|
const downloadUrl = window.URL.createObjectURL(blob);
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = downloadUrl;
|
link.href = downloadUrl;
|
||||||
link.download = title
|
link.download = downloadFileNameForPhoto(photo);
|
||||||
? title.replace(/[^a-z0-9]/gi, '_').toLowerCase()
|
|
||||||
: url.split('/').pop() || 'download';
|
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
|
|||||||
@ -10,7 +10,6 @@ export default function PathLoaderButton({
|
|||||||
loaderDelay = 100,
|
loaderDelay = 100,
|
||||||
shouldScroll = true,
|
shouldScroll = true,
|
||||||
shouldReplace,
|
shouldReplace,
|
||||||
handleAction,
|
|
||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
@ -19,7 +18,6 @@ export default function PathLoaderButton({
|
|||||||
loaderDelay?: number
|
loaderDelay?: number
|
||||||
shouldScroll?: boolean
|
shouldScroll?: boolean
|
||||||
shouldReplace?: boolean
|
shouldReplace?: boolean
|
||||||
handleAction?: () => Promise<void>
|
|
||||||
} & ComponentProps<typeof LoaderButton>) {
|
} & ComponentProps<typeof LoaderButton>) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
@ -48,15 +46,11 @@ export default function PathLoaderButton({
|
|||||||
<LoaderButton
|
<LoaderButton
|
||||||
{...props}
|
{...props}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
startTransition(async () => {
|
startTransition(() => {
|
||||||
if (handleAction) {
|
if (shouldReplace) {
|
||||||
await handleAction();
|
router.replace(path, { scroll: shouldScroll });
|
||||||
} else {
|
} else {
|
||||||
if (shouldReplace) {
|
router.push(path, { scroll: shouldScroll });
|
||||||
router.replace(path, { scroll: shouldScroll });
|
|
||||||
} else {
|
|
||||||
router.push(path, { scroll: shouldScroll });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import {
|
|||||||
formatExposureCompensation,
|
formatExposureCompensation,
|
||||||
formatExposureTime,
|
formatExposureTime,
|
||||||
} from '@/utility/exif';
|
} from '@/utility/exif';
|
||||||
|
import { parameterize } from '@/utility/string';
|
||||||
import camelcaseKeys from 'camelcase-keys';
|
import camelcaseKeys from 'camelcase-keys';
|
||||||
import { isBefore } from 'date-fns';
|
import { isBefore } from 'date-fns';
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
@ -311,5 +312,10 @@ export const isNextImageReadyBasedOnPhotos = async (photos: Photo[]) =>
|
|||||||
.then(response => response.ok)
|
.then(response => response.ok)
|
||||||
.catch(() => false);
|
.catch(() => false);
|
||||||
|
|
||||||
|
export const downloadFileNameForPhoto = (photo: Photo) =>
|
||||||
|
photo.title
|
||||||
|
? `${parameterize(photo.title)}.${photo.extension}`
|
||||||
|
: photo.url.split('/').pop() || 'download';
|
||||||
|
|
||||||
export const doesPhotoNeedBlurCompatibility = (photo: Photo) =>
|
export const doesPhotoNeedBlurCompatibility = (photo: Photo) =>
|
||||||
isBefore(photo.updatedAt, new Date('2024-05-07'));
|
isBefore(photo.updatedAt, new Date('2024-05-07'));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user