Adding public download button option

This commit is contained in:
si1k 2024-08-19 15:14:38 -04:00
parent 6b417d56c9
commit cf446b29e3
6 changed files with 80 additions and 4 deletions

View File

@ -113,6 +113,7 @@ Application behavior can be changed by configuring the following environment var
- `NEXT_PUBLIC_HIDE_TITLE_FALLBACK_TEXT = 1` prevents showing "Untitled" for photos without titles
- `NEXT_PUBLIC_IGNORE_PRIORITY_ORDER = 1` prevents `priority_order` field affecting photo order
- `NEXT_PUBLIC_PUBLIC_API = 1` enables public API available at `/api`
- `NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS = 1` enables public image downloads
- `NEXT_PUBLIC_HIDE_REPO_LINK = 1` removes footer link to repo
- `NEXT_PUBLIC_HIDE_SOCIAL = 1` removes X button from share modal
- `NEXT_PUBLIC_HIDE_FILM_SIMULATIONS = 1` prevents Fujifilm simulations showing up in `/grid` sidebar and CMD-K search results

View File

@ -0,0 +1,46 @@
import { MdOutlineFileDownload } from 'react-icons/md';
import PathLoaderButton from './primitives/PathLoaderButton';
import { clsx } from 'clsx/lite';
import { Photo } from '@/photo';
export default function DownloadButton({
photo,
dim,
className,
}: {
photo: Photo
dim?: boolean
className?: string
}) {
const {url, title} = photo;
return (
<PathLoaderButton
path={url}
className={clsx(
className,
dim ? 'text-dim' : 'text-medium',
'-mx-0.5 translate-x-0.5',
'sm:mx-0 sm:translate-x-0'
)}
icon={<MdOutlineFileDownload size={16} />}
spinnerColor='dim'
styleAs='link'
shouldReplace
handleAction={async () => {
const response = await fetch(url);
const blob = await response.blob();
const downloadUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = downloadUrl;
link.download = title
? title.replace(/[^a-z0-9]/gi, '_').toLowerCase()
: url.split('/').pop() || 'download';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(downloadUrl);
}}
/>
);
}

View File

@ -10,6 +10,7 @@ export default function PathLoaderButton({
loaderDelay = 100,
shouldScroll = true,
shouldReplace,
handleAction,
children,
...props
}: {
@ -18,6 +19,7 @@ export default function PathLoaderButton({
loaderDelay?: number
shouldScroll?: boolean
shouldReplace?: boolean
handleAction?: () => Promise<void>
} & ComponentProps<typeof LoaderButton>) {
const router = useRouter();
@ -46,11 +48,15 @@ export default function PathLoaderButton({
<LoaderButton
{...props}
onClick={() => {
startTransition(() => {
if (shouldReplace) {
router.replace(path, { scroll: shouldScroll });
startTransition(async () => {
if (handleAction) {
await handleAction();
} else {
router.push(path, { scroll: shouldScroll });
if (shouldReplace) {
router.replace(path, { scroll: shouldScroll });
} else {
router.push(path, { scroll: shouldScroll });
}
}
});
}}

View File

@ -19,6 +19,7 @@ import {
} from '@/site/paths';
import PhotoTags from '@/tag/PhotoTags';
import ShareButton from '@/components/ShareButton';
import DownloadButton from '@/components/DownloadButton';
import PhotoCamera from '../camera/PhotoCamera';
import { cameraFromPhoto } from '@/camera';
import PhotoFilmSimulation from '@/simulation/PhotoFilmSimulation';
@ -28,6 +29,7 @@ import PhotoLink from './PhotoLink';
import {
SHOULD_PREFETCH_ALL_LINKS,
SHOW_PHOTO_TITLE_FALLBACK_TEXT,
ALLOW_PUBLIC_DOWNLOADS,
} from '@/site/config';
import AdminPhotoMenuClient from '@/admin/AdminPhotoMenuClient';
import { RevalidatePhoto } from './InfinitePhotoScroll';
@ -228,6 +230,14 @@ export default function PhotoLarge({
!hasNonDateContent && isUserSignedIn && 'md:pr-7',
)}
/>
{ALLOW_PUBLIC_DOWNLOADS &&
<DownloadButton
className={clsx(
'md:translate-x-[-2.5px]',
'translate-y-[1.5px] md:translate-y-0',
)}
photo={photo}
/>}
{shouldShare &&
<ShareButton
className={clsx(

View File

@ -65,6 +65,7 @@ export default function SiteChecklistClient({
aiTextAutoGeneratedFields,
hasAiTextAutoGeneratedFields,
isPublicApiEnabled,
isPublicDownloadsEnabled,
isOgTextBottomAligned,
gridAspectRatio,
hasGridAspectRatio,
@ -533,6 +534,15 @@ export default function SiteChecklistClient({
a public API available at <code>/api</code>:
{renderEnvVars(['NEXT_PUBLIC_PUBLIC_API'])}
</ChecklistRow>
<ChecklistRow
title="Public downloads"
status={isPublicDownloadsEnabled}
optional
>
Set environment variable to {'"1"'} to enable
public downloads of photos:
{renderEnvVars(['NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS'])}
</ChecklistRow>
<ChecklistRow
title="Show repo link"
status={showRepoLink}

View File

@ -149,6 +149,8 @@ export const PRIORITY_ORDER_ENABLED =
process.env.NEXT_PUBLIC_IGNORE_PRIORITY_ORDER !== '1';
export const PUBLIC_API_ENABLED =
process.env.NEXT_PUBLIC_PUBLIC_API === '1';
export const ALLOW_PUBLIC_DOWNLOADS =
process.env.NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS === '1';
export const SHOW_REPO_LINK =
process.env.NEXT_PUBLIC_HIDE_REPO_LINK !== '1';
export const SHOW_SOCIAL =
@ -226,6 +228,7 @@ export const CONFIG_CHECKLIST_STATUS = {
Boolean(process.env.AI_TEXT_AUTO_GENERATED_FIELDS),
isPriorityOrderEnabled: PRIORITY_ORDER_ENABLED,
isPublicApiEnabled: PUBLIC_API_ENABLED,
isPublicDownloadsEnabled: ALLOW_PUBLIC_DOWNLOADS,
isOgTextBottomAligned: OG_TEXT_BOTTOM_ALIGNMENT,
gridAspectRatio: GRID_ASPECT_RATIO,
hasGridAspectRatio: Boolean(process.env.NEXT_PUBLIC_GRID_ASPECT_RATIO),