diff --git a/src/components/cmdk/CommandKClient.tsx b/src/components/cmdk/CommandKClient.tsx
index 4e1e21c2..4f409c1f 100644
--- a/src/components/cmdk/CommandKClient.tsx
+++ b/src/components/cmdk/CommandKClient.tsx
@@ -31,7 +31,7 @@ import { useTheme } from 'next-themes';
import { BiDesktop, BiMoon, BiSun } from 'react-icons/bi';
import { IoInvertModeSharp } from 'react-icons/io5';
import { useAppState } from '@/state/AppState';
-import { queryPhotosByTitleAction } from '@/photo/actions';
+import { searchPhotosAction } from '@/photo/actions';
import { RiToolsFill } from 'react-icons/ri';
import { BiLockAlt, BiSolidUser } from 'react-icons/bi';
import { HiDocumentText } from 'react-icons/hi';
@@ -151,27 +151,33 @@ export default function CommandKClient({
useEffect(() => {
if (queryDebounced.length >= MINIMUM_QUERY_LENGTH && !isPending) {
setIsLoading(true);
- queryPhotosByTitleAction(queryDebounced).then(photos => {
- if (isOpenRef.current) {
- setQueriedSections(photos.length > 0
- ? [{
- heading: 'Photos',
- accessory: ,
- items: photos.map(photo => ({
- label: titleForPhoto(photo),
- keywords: getKeywordsForPhoto(photo),
- annotation: ,
- accessory: ,
- path: pathForPhoto({ photo }),
- })),
- }]
- : []);
- } else {
- // Ignore stale requests that come in after dialog is closed
+ searchPhotosAction(queryDebounced)
+ .then(photos => {
+ if (isOpenRef.current) {
+ setQueriedSections(photos.length > 0
+ ? [{
+ heading: 'Photos',
+ accessory: ,
+ items: photos.map(photo => ({
+ label: titleForPhoto(photo),
+ keywords: getKeywordsForPhoto(photo),
+ annotation: ,
+ accessory: ,
+ path: pathForPhoto({ photo }),
+ })),
+ }]
+ : []);
+ } else {
+ // Ignore stale requests that come in after dialog is closed
+ setQueriedSections([]);
+ }
+ setIsLoading(false);
+ })
+ .catch(e => {
+ console.error(e);
setQueriedSections([]);
- }
- setIsLoading(false);
- });
+ setIsLoading(false);
+ });
}
}, [queryDebounced, isPending]);
diff --git a/src/photo/PhotoHeader.tsx b/src/photo/PhotoHeader.tsx
index d798eedf..f66508df 100644
--- a/src/photo/PhotoHeader.tsx
+++ b/src/photo/PhotoHeader.tsx
@@ -6,6 +6,7 @@ import {
PhotoDateRange,
PhotoSetAttributes,
dateRangeForPhotos,
+ titleForPhoto,
} from '.';
import ShareButton from '@/components/ShareButton';
import AnimateItems from '@/components/AnimateItems';
@@ -13,7 +14,6 @@ import { ReactNode } from 'react';
import DivDebugBaselineGrid from '@/components/DivDebugBaselineGrid';
import PhotoPrevNext from './PhotoPrevNext';
import PhotoLink from './PhotoLink';
-import { formatDate } from '@/utility/date';
import ResponsiveText from '@/components/primitives/ResponsiveText';
import { useAppState } from '@/state/AppState';
@@ -83,10 +83,7 @@ export default function PhotoHeader({
photo={selectedPhoto}
className="uppercase font-bold text-ellipsis truncate"
>
- {
- selectedPhoto.title ||
- formatDate(selectedPhoto.takenAt, 'tiny')
- }
+ {titleForPhoto(selectedPhoto, true)}
);
return (
diff --git a/src/photo/actions.ts b/src/photo/actions.ts
index 734208c0..3d3244a6 100644
--- a/src/photo/actions.ts
+++ b/src/photo/actions.ts
@@ -35,7 +35,7 @@ import {
} from '@/site/paths';
import { blurImageFromUrl, extractImageDataFromBlobPath } from './server';
import { TAG_FAVS, isTagFavs } from '@/tag';
-import { convertPhotoToPhotoDbInsert } from '.';
+import { convertPhotoToPhotoDbInsert, Photo } from '.';
import { runAuthenticatedAdminServerAction } from '@/auth';
import { AI_IMAGE_QUERIES, AiImageQuery } from './ai';
import { streamOpenAiImageQuery } from '@/services/openai';
@@ -412,6 +412,9 @@ export const getPhotosCachedAction = async (options: GetPhotosOptions) =>
// Public actions
-export const queryPhotosByTitleAction = async (query: string) =>
- (await getPhotos({ query, limit: 10 }))
- .filter(({ title }) => Boolean(title));
+export const searchPhotosAction = async (query: string) =>
+ getPhotos({ query, limit: 10 })
+ .catch(e => {
+ console.error('Could not query photos', e);
+ return [] as Photo[];
+ });
diff --git a/src/photo/index.ts b/src/photo/index.ts
index bf3feab3..e3890a20 100644
--- a/src/photo/index.ts
+++ b/src/photo/index.ts
@@ -5,7 +5,7 @@ import { getNextImageUrlForRequest } from '@/services/next-image';
import { FilmSimulation } from '@/simulation';
import { HIGH_DENSITY_GRID, SHOW_EXIF_DATA } from '@/site/config';
import { ABSOLUTE_PATH_FOR_HOME_IMAGE } from '@/site/paths';
-import { formatDateFromPostgresString } from '@/utility/date';
+import { formatDate, formatDateFromPostgresString } from '@/utility/date';
import {
formatAperture,
formatIso,
@@ -198,8 +198,18 @@ const PHOTO_ID_FORWARDING_TABLE: Record = JSON.parse(
export const translatePhotoId = (id: string) =>
PHOTO_ID_FORWARDING_TABLE[id] || id;
-export const titleForPhoto = (photo: Photo) =>
- photo.title || 'Untitled';
+export const titleForPhoto = (
+ photo: Photo,
+ preferDateOverUntitled?: boolean,
+) => {
+ if (photo.title) {
+ return photo.title;
+ } else if (preferDateOverUntitled && (photo.takenAt || photo.createdAt)) {
+ return formatDate(photo.takenAt || photo.createdAt, 'tiny');
+ } else {
+ return 'Untitled';
+ }
+};
export const altTextForPhoto = (photo: Photo) =>
photo.semanticDescription || titleForPhoto(photo);