Adding public download button option
This commit is contained in:
parent
6b417d56c9
commit
cf446b29e3
@ -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
|
||||
|
||||
46
src/components/DownloadButton.tsx
Normal file
46
src/components/DownloadButton.tsx
Normal 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);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -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 });
|
||||
}
|
||||
}
|
||||
});
|
||||
}}
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user