Make photo titles optional
This commit is contained in:
parent
8f984e69ef
commit
c4044801a1
@ -63,7 +63,10 @@ export default async function AdminPage() {
|
||||
href={routeForPhoto(photo)}
|
||||
className="grow flex items-center gap-2"
|
||||
>
|
||||
{photo.title}
|
||||
{photo.title ||
|
||||
<span className="text-gray-400 dark:text-gray-500">
|
||||
Untitled
|
||||
</span>}
|
||||
{photo.priorityOrder !== null &&
|
||||
<span className={cc(
|
||||
'text-xs leading-none px-1.5 py-1 rounded-sm',
|
||||
@ -101,7 +104,7 @@ function AdminGrid ({
|
||||
title: string,
|
||||
children: ReactNode,
|
||||
}) {
|
||||
return <div className="space-y-2">
|
||||
return <div className="space-y-4">
|
||||
<div className="font-bold">
|
||||
{title}
|
||||
</div>
|
||||
|
||||
@ -2,7 +2,11 @@ import { PropsWithChildren } from 'react';
|
||||
import AnimateItems from '@/components/AnimateItems';
|
||||
import PhotoLinks from '@/photo/PhotoLinks';
|
||||
import SiteGrid from '@/components/SiteGrid';
|
||||
import { ogImageDescriptionForPhoto, ogImageUrlForPhoto } from '@/photo';
|
||||
import {
|
||||
ogImageDescriptionForPhoto,
|
||||
ogImageUrlForPhoto,
|
||||
titleForPhoto,
|
||||
} from '@/photo';
|
||||
import PhotoGrid from '@/photo/PhotoGrid';
|
||||
import PhotoLarge from '@/photo/PhotoLarge';
|
||||
import { cc } from '@/utility/css';
|
||||
@ -24,7 +28,7 @@ export async function generateMetadata(
|
||||
|
||||
if (!photo) { return {}; }
|
||||
|
||||
const title = photo.title;
|
||||
const title = titleForPhoto(photo);
|
||||
const description = ogImageDescriptionForPhoto(photo);
|
||||
const images = ogImageUrlForPhoto(photo);
|
||||
|
||||
|
||||
@ -72,9 +72,8 @@ export default function PhotoForm({
|
||||
};
|
||||
}, [url, type]);
|
||||
|
||||
const isFormValid =
|
||||
Boolean(formData.blurData) &&
|
||||
Boolean(formData.title);
|
||||
const isFormValid = FORM_METADATA_ENTRIES.every(([key, { required }]) =>
|
||||
!required || Boolean(formData[key]));
|
||||
|
||||
return (
|
||||
<div className="space-y-8 max-w-[38rem]">
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Photo } from '.';
|
||||
import { Photo, titleForPhoto } from '.';
|
||||
import SiteGrid from '@/components/SiteGrid';
|
||||
import ImageLarge from '@/components/ImageLarge';
|
||||
import { cc } from '@/utility/css';
|
||||
@ -30,7 +30,7 @@ export default function PhotoLarge({
|
||||
contentMain={
|
||||
<ImageLarge
|
||||
className="w-full"
|
||||
alt={photo.title ?? 'Photo'}
|
||||
alt={titleForPhoto(photo)}
|
||||
href={routeForPhoto(photo)}
|
||||
src={photo.url}
|
||||
aspectRatio={photo.aspectRatio}
|
||||
@ -50,7 +50,7 @@ export default function PhotoLarge({
|
||||
href={routeForPhoto(photo)}
|
||||
className="font-bold uppercase"
|
||||
>
|
||||
{photo.title}
|
||||
{titleForPhoto(photo)}
|
||||
</Link>
|
||||
<div className="uppercase">
|
||||
{photo.make} {photo.model}
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Photo, ogImageDescriptionForPhoto, ogImageUrlForPhoto } from '@/photo';
|
||||
import {
|
||||
Photo,
|
||||
ogImageDescriptionForPhoto,
|
||||
ogImageUrlForPhoto,
|
||||
titleForPhoto,
|
||||
} from '@/photo';
|
||||
import { cc } from '@/utility/css';
|
||||
import Link from 'next/link';
|
||||
import { BiError } from 'react-icons/bi';
|
||||
@ -111,7 +116,7 @@ export default function PhotoOGTile({
|
||||
'border-t border-gray-200 dark:border-gray-800',
|
||||
)}>
|
||||
<div className="text-gray-800 dark:text-white font-medium">
|
||||
{photo.title}
|
||||
{titleForPhoto(photo)}
|
||||
</div>
|
||||
<div className="text-gray-500">
|
||||
{ogImageDescriptionForPhoto(photo)}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Photo } from '.';
|
||||
import { Photo, titleForPhoto } from '.';
|
||||
import ImageSmall from '@/components/ImageSmall';
|
||||
import Link from 'next/link';
|
||||
import { cc } from '@/utility/css';
|
||||
@ -24,7 +24,7 @@ export default function PhotoSmall({
|
||||
aspectRatio={photo.aspectRatio}
|
||||
blurData={photo.blurData}
|
||||
className="w-full"
|
||||
alt={photo.title ?? 'Photo'}
|
||||
alt={titleForPhoto(photo)}
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Photo } from '.';
|
||||
import { Photo, titleForPhoto } from '.';
|
||||
import ImageTiny from '@/components/ImageTiny';
|
||||
import Link from 'next/link';
|
||||
import { cc } from '@/utility/css';
|
||||
@ -27,7 +27,7 @@ export default function PhotoTiny({
|
||||
src={photo.url}
|
||||
aspectRatio={photo.aspectRatio}
|
||||
blurData={photo.blurData}
|
||||
alt={photo.title ?? 'Photo'}
|
||||
alt={titleForPhoto(photo)}
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
|
||||
@ -18,13 +18,13 @@ type FormMeta = {
|
||||
};
|
||||
|
||||
const FORM_METADATA: Record<keyof PhotoFormData, FormMeta> = {
|
||||
title: { label: 'title', required: true },
|
||||
title: { label: 'title' },
|
||||
id: { label: 'id', readOnly: true, hideIfEmpty: true },
|
||||
idShort: { label: 'short id', readOnly: true, hideIfEmpty: true },
|
||||
url: { label: 'url', readOnly: true },
|
||||
extension: { label: 'extension', readOnly: true },
|
||||
aspectRatio: { label: 'aspect ratio', readOnly: true },
|
||||
blurData: { label: 'blur data', readOnly: true },
|
||||
blurData: { label: 'blur data', readOnly: true, required: true },
|
||||
make: { label: 'camera make' },
|
||||
model: { label: 'camera model' },
|
||||
focalLength: { label: 'focal length' },
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { getNextImageUrlForRequest } from '@/utility/image';
|
||||
import { Photo } from '..';
|
||||
import { Photo, titleForPhoto } from '..';
|
||||
|
||||
const IMAGE_WIDTH = 400;
|
||||
|
||||
@ -38,7 +38,7 @@ export default function PhotoGridImageResponse({
|
||||
request,
|
||||
IMAGE_WIDTH,
|
||||
)}
|
||||
alt={photo.title}
|
||||
alt={titleForPhoto(photo)}
|
||||
width={IMAGE_WIDTH}
|
||||
height={IMAGE_WIDTH / photo.aspectRatio}
|
||||
style={{
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Photo } from '..';
|
||||
import { Photo, titleForPhoto } from '..';
|
||||
import { getNextImageUrlForRequest } from '@/utility/image';
|
||||
import { formatModelShort } from '@/utility/exif';
|
||||
import { AiFillApple } from 'react-icons/ai';
|
||||
@ -34,7 +34,7 @@ export default function PhotoOGImageResponse({
|
||||
)}
|
||||
width={width}
|
||||
height={width / photo.aspectRatio}
|
||||
alt={photo.title}
|
||||
alt={titleForPhoto(photo)}
|
||||
/>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
|
||||
@ -140,3 +140,6 @@ export const translatePhotoId = (shortId: string) => {
|
||||
const id = PHOTO_ID_FORWARDING_TABLE[shortId] || shortId;
|
||||
return id.length === 22 ? translator.toUUID(id) : id;
|
||||
};
|
||||
|
||||
export const titleForPhoto = (photo: Photo) =>
|
||||
photo.title || 'Untitled';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user