Propagate recipe titles on photo create/update
This commit is contained in:
parent
f6d8e452f0
commit
81b127468f
@ -63,6 +63,7 @@ import { IoMdCamera } from 'react-icons/io';
|
||||
import { labelForFilmSimulation } from '@/platforms/fujifilm/simulation';
|
||||
import PhotoFilmSimulationIcon from '@/simulation/PhotoFilmSimulationIcon';
|
||||
import { formatFocalLength } from '@/focal';
|
||||
import { formatRecipe } from '@/recipe';
|
||||
|
||||
const DIALOG_TITLE = 'Global Command-K Menu';
|
||||
const DIALOG_DESCRIPTION = 'For searching photos, views, and settings';
|
||||
@ -279,7 +280,7 @@ export default function CommandKClient({
|
||||
className="translate-x-[-1px]"
|
||||
/>,
|
||||
items: recipes.map(({ recipe, count }) => ({
|
||||
label: recipe,
|
||||
label: formatRecipe(recipe),
|
||||
annotation: formatCount(count),
|
||||
annotationAria: formatCountDescriptive(count),
|
||||
path: pathForRecipe(recipe),
|
||||
|
||||
@ -37,6 +37,7 @@ import {
|
||||
blurImageFromUrl,
|
||||
convertFormDataToPhotoDbInsertAndLookupRecipeTitle,
|
||||
extractImageDataFromBlobPath,
|
||||
propagateRecipeTitleIfNecessary,
|
||||
} from './server';
|
||||
import { TAG_FAVS, isTagFavs } from '@/tag';
|
||||
import { convertPhotoToPhotoDbInsert, Photo } from '.';
|
||||
@ -73,6 +74,7 @@ export const createPhotoAction = async (formData: FormData) =>
|
||||
if (updatedUrl) {
|
||||
photo.url = updatedUrl;
|
||||
await insertPhoto(photo);
|
||||
await propagateRecipeTitleIfNecessary(formData, photo);
|
||||
revalidateAllKeysAndPaths();
|
||||
redirect(PATH_ADMIN_PHOTOS);
|
||||
}
|
||||
@ -210,7 +212,10 @@ export const updatePhotoAction = async (formData: FormData) =>
|
||||
|
||||
await updatePhoto(photo)
|
||||
.then(async () => {
|
||||
if (urlToDelete) { await deleteFile(urlToDelete); }
|
||||
if (urlToDelete) {
|
||||
await deleteFile(urlToDelete);
|
||||
}
|
||||
await propagateRecipeTitleIfNecessary(formData, photo);
|
||||
});
|
||||
|
||||
revalidatePhoto(photo.id);
|
||||
|
||||
@ -317,6 +317,55 @@ export const getUniqueCameras = async () =>
|
||||
})))
|
||||
, 'getUniqueCameras');
|
||||
|
||||
export const getUniqueRecipes = async () =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
SELECT DISTINCT recipe_title, COUNT(*)
|
||||
FROM photos
|
||||
WHERE hidden IS NOT TRUE AND recipe_title IS NOT NULL
|
||||
GROUP BY recipe_title
|
||||
ORDER BY recipe_title ASC
|
||||
`.then(({ rows }): Recipes => rows
|
||||
.map(({ recipe_title, count }) => ({
|
||||
recipe: recipe_title,
|
||||
count: parseInt(count, 10),
|
||||
})))
|
||||
, 'getUniqueRecipes');
|
||||
|
||||
export const getRecipeTitleForData = async (data: string | object) =>
|
||||
// Includes legacy check on pre-stringified JSON
|
||||
safelyQueryPhotos(() => sql`
|
||||
SELECT recipe_title FROM photos
|
||||
WHERE hidden IS NOT TRUE AND
|
||||
recipe_data = ${typeof data === 'string' ? data : JSON.stringify(data)}
|
||||
LIMIT 1
|
||||
`
|
||||
.then(({ rows }) => rows[0]?.recipe_title as string | undefined)
|
||||
, 'getRecipeTitleForData');
|
||||
|
||||
export const updateAllMatchingRecipeTitles = (
|
||||
title: string,
|
||||
data: string,
|
||||
) =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
UPDATE photos
|
||||
SET recipe_title = ${title}
|
||||
WHERE recipe_title IS NULL AND recipe_data = ${data}
|
||||
`, 'updateAllMatchingRecipeTitles');
|
||||
|
||||
export const getUniqueFilmSimulations = async () =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
SELECT DISTINCT film_simulation, COUNT(*)
|
||||
FROM photos
|
||||
WHERE hidden IS NOT TRUE AND film_simulation IS NOT NULL
|
||||
GROUP BY film_simulation
|
||||
ORDER BY film_simulation ASC
|
||||
`.then(({ rows }): FilmSimulations => rows
|
||||
.map(({ film_simulation, count }) => ({
|
||||
simulation: film_simulation as FilmSimulation,
|
||||
count: parseInt(count, 10),
|
||||
})))
|
||||
, 'getUniqueFilmSimulations');
|
||||
|
||||
export const getUniqueLenses = async () =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
SELECT DISTINCT lens_make||' '||lens_model as lens,
|
||||
@ -333,45 +382,7 @@ export const getUniqueLenses = async () =>
|
||||
lens: { make, model },
|
||||
count: parseInt(count, 10),
|
||||
})))
|
||||
, 'getUniqueCameras');
|
||||
|
||||
export const getUniqueFilmSimulations = async () =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
SELECT DISTINCT film_simulation, COUNT(*)
|
||||
FROM photos
|
||||
WHERE hidden IS NOT TRUE AND film_simulation IS NOT NULL
|
||||
GROUP BY film_simulation
|
||||
ORDER BY film_simulation ASC
|
||||
`.then(({ rows }): FilmSimulations => rows
|
||||
.map(({ film_simulation, count }) => ({
|
||||
simulation: film_simulation as FilmSimulation,
|
||||
count: parseInt(count, 10),
|
||||
})))
|
||||
, 'getUniqueFilmSimulations');
|
||||
|
||||
export const getUniqueRecipes = async () =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
SELECT DISTINCT recipe_title, COUNT(*)
|
||||
FROM photos
|
||||
WHERE hidden IS NOT TRUE AND recipe_title IS NOT NULL
|
||||
GROUP BY recipe_title
|
||||
ORDER BY recipe_title ASC
|
||||
`.then(({ rows }): Recipes => rows
|
||||
.map(({ recipe_title, count }) => ({
|
||||
recipe: recipe_title,
|
||||
count: parseInt(count, 10),
|
||||
})))
|
||||
, 'getUniqueRecipes');
|
||||
|
||||
export const getRecipeTitleForData = async (data: string | object) =>
|
||||
safelyQueryPhotos(() => query(
|
||||
// eslint-disable-next-line max-len
|
||||
'SELECT recipe_title FROM photos WHERE hidden IS NOT TRUE AND recipe_data = $1 LIMIT 1',
|
||||
// Legacy check on escaped, string-based JSON
|
||||
[typeof data === 'string' ? data : JSON.stringify(data)],
|
||||
)
|
||||
.then(({ rows }) => rows[0]?.recipe_title as string | undefined)
|
||||
, 'getRecipeTitleForData');
|
||||
, 'getUniqueLenses');
|
||||
|
||||
export const getUniqueFocalLengths = async () =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
|
||||
@ -22,7 +22,11 @@ import {
|
||||
FujifilmRecipe,
|
||||
getFujifilmRecipeFromMakerNote,
|
||||
} from '@/platforms/fujifilm/recipe';
|
||||
import { getRecipeTitleForData } from './db/query';
|
||||
import {
|
||||
getRecipeTitleForData,
|
||||
updateAllMatchingRecipeTitles,
|
||||
} from './db/query';
|
||||
import { PhotoDbInsert } from '.';
|
||||
const IMAGE_WIDTH_RESIZE = 200;
|
||||
const IMAGE_WIDTH_BLUR = 200;
|
||||
|
||||
@ -211,3 +215,20 @@ export const convertFormDataToPhotoDbInsertAndLookupRecipeTitle =
|
||||
|
||||
return photo;
|
||||
};
|
||||
|
||||
export const propagateRecipeTitleIfNecessary = async (
|
||||
formData: FormData,
|
||||
photo: PhotoDbInsert,
|
||||
) => {
|
||||
if (
|
||||
// Only propagate recipe title if set by user before lookup
|
||||
formData.get('recipeTitle') &&
|
||||
photo.recipeTitle &&
|
||||
photo.recipeData
|
||||
) {
|
||||
await updateAllMatchingRecipeTitles(
|
||||
photo.recipeTitle,
|
||||
photo.recipeData,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user