Refine camera/lens query handling
This commit is contained in:
parent
0ca8823dae
commit
3966a6437a
10
__tests__/lens.test.ts
Normal file
10
__tests__/lens.test.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/* eslint-disable max-len */
|
||||||
|
import { formatLensText, Lens } from '@/lens';
|
||||||
|
|
||||||
|
const IPHONE_15_PRO_FRONT: Lens = { make: 'Apple', model: 'iPhone 15 Pro front TrueDepth camera 2.69mm f/1.9' };
|
||||||
|
|
||||||
|
describe('Lens', () => {
|
||||||
|
it('correctly formats iPhone lenses', () => {
|
||||||
|
expect(formatLensText(IPHONE_15_PRO_FRONT)).toBe('Front Camera');
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -7,7 +7,6 @@ import PhotoSmall from '@/photo/PhotoSmall';
|
|||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
import { pathForAdminPhotoEdit, pathForPhoto } from '@/app/paths';
|
import { pathForAdminPhotoEdit, pathForPhoto } from '@/app/paths';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { AiOutlineEyeInvisible } from 'react-icons/ai';
|
|
||||||
import PhotoDate from '@/photo/PhotoDate';
|
import PhotoDate from '@/photo/PhotoDate';
|
||||||
import EditButton from './EditButton';
|
import EditButton from './EditButton';
|
||||||
import { useAppState } from '@/state/AppState';
|
import { useAppState } from '@/state/AppState';
|
||||||
@ -15,6 +14,7 @@ import { RevalidatePhoto } from '@/photo/InfinitePhotoScroll';
|
|||||||
import PhotoSyncButton from './PhotoSyncButton';
|
import PhotoSyncButton from './PhotoSyncButton';
|
||||||
import DeletePhotoButton from './DeletePhotoButton';
|
import DeletePhotoButton from './DeletePhotoButton';
|
||||||
import { Timezone } from '@/utility/timezone';
|
import { Timezone } from '@/utility/timezone';
|
||||||
|
import IconHidden from '@/components/icons/IconHidden';
|
||||||
|
|
||||||
export default function AdminPhotosTable({
|
export default function AdminPhotosTable({
|
||||||
photos,
|
photos,
|
||||||
@ -71,7 +71,7 @@ export default function AdminPhotosTable({
|
|||||||
{titleForPhoto(photo)}
|
{titleForPhoto(photo)}
|
||||||
{photo.hidden && <span className="whitespace-nowrap">
|
{photo.hidden && <span className="whitespace-nowrap">
|
||||||
{' '}
|
{' '}
|
||||||
<AiOutlineEyeInvisible
|
<IconHidden
|
||||||
className="inline translate-y-[-0.5px]"
|
className="inline translate-y-[-0.5px]"
|
||||||
size={16}
|
size={16}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -140,13 +140,13 @@ export const pathForTag = (tag: string) =>
|
|||||||
`${PREFIX_TAG}/${tag}`;
|
`${PREFIX_TAG}/${tag}`;
|
||||||
|
|
||||||
export const pathForCamera = ({ make, model }: Camera) =>
|
export const pathForCamera = ({ make, model }: Camera) =>
|
||||||
`${PREFIX_CAMERA}/${parameterize(make, true)}/${parameterize(model, true)}`;
|
`${PREFIX_CAMERA}/${parameterize(make)}/${parameterize(model)}`;
|
||||||
|
|
||||||
export const pathForFilmSimulation = (simulation: FilmSimulation) =>
|
export const pathForFilmSimulation = (simulation: FilmSimulation) =>
|
||||||
`${PREFIX_FILM_SIMULATION}/${simulation}`;
|
`${PREFIX_FILM_SIMULATION}/${simulation}`;
|
||||||
|
|
||||||
export const pathForLens = ({ make, model }: Lens) =>
|
export const pathForLens = ({ make, model }: Lens) =>
|
||||||
`${PREFIX_LENS}/${parameterize(make, true)}/${parameterize(model, true)}`;
|
`${PREFIX_LENS}/${parameterize(make)}/${parameterize(model)}`;
|
||||||
|
|
||||||
export const pathForFocalLength = (focal: number) =>
|
export const pathForFocalLength = (focal: number) =>
|
||||||
`${PREFIX_FOCAL_LENGTH}/${focal}mm`;
|
`${PREFIX_FOCAL_LENGTH}/${focal}mm`;
|
||||||
|
|||||||
@ -28,7 +28,7 @@ export type Cameras = CameraWithCount[];
|
|||||||
|
|
||||||
// Support keys for make-only and model-only camera queries
|
// Support keys for make-only and model-only camera queries
|
||||||
export const createCameraKey = ({ make, model }: Partial<Camera>) =>
|
export const createCameraKey = ({ make, model }: Partial<Camera>) =>
|
||||||
parameterize(`${make ?? 'ANY'}-${model ?? 'ANY'}`, true);
|
parameterize(`${make ?? 'ANY'}-${model ?? 'ANY'}`);
|
||||||
|
|
||||||
export const getCameraFromParams = ({
|
export const getCameraFromParams = ({
|
||||||
make,
|
make,
|
||||||
@ -37,8 +37,8 @@ export const getCameraFromParams = ({
|
|||||||
make: string,
|
make: string,
|
||||||
model: string,
|
model: string,
|
||||||
}): Camera => ({
|
}): Camera => ({
|
||||||
make: parameterize(make, true),
|
make: parameterize(make),
|
||||||
model: parameterize(model, true),
|
model: parameterize(model),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const sortCamerasWithCount = (
|
export const sortCamerasWithCount = (
|
||||||
|
|||||||
@ -28,7 +28,7 @@ export type Lenses = LensWithCount[];
|
|||||||
|
|
||||||
// Support keys for make-only and model-only lens queries
|
// Support keys for make-only and model-only lens queries
|
||||||
export const createLensKey = ({ make, model }: Partial<Lens>) =>
|
export const createLensKey = ({ make, model }: Partial<Lens>) =>
|
||||||
parameterize(`${make ?? 'ANY'}-${model ?? 'ANY'}`, true);
|
parameterize(`${make ?? 'ANY'}-${model ?? 'ANY'}`);
|
||||||
|
|
||||||
export const getLensFromParams = ({
|
export const getLensFromParams = ({
|
||||||
make,
|
make,
|
||||||
@ -37,8 +37,8 @@ export const getLensFromParams = ({
|
|||||||
make: string,
|
make: string,
|
||||||
model: string,
|
model: string,
|
||||||
}): Lens => ({
|
}): Lens => ({
|
||||||
make: parameterize(make, true),
|
make: parameterize(make),
|
||||||
model: parameterize(model, true),
|
model: parameterize(model),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const lensFromPhoto = (
|
export const lensFromPhoto = (
|
||||||
@ -55,13 +55,20 @@ const isLensMakeApple = (make?: string) =>
|
|||||||
export const isLensApple = ({ make }: Lens) =>
|
export const isLensApple = ({ make }: Lens) =>
|
||||||
isLensMakeApple(make);
|
isLensMakeApple(make);
|
||||||
|
|
||||||
|
const formatAppleLensText = (model: string) => {
|
||||||
|
if (model.includes('front TrueDepth camera')) {
|
||||||
|
return 'Front Camera';
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
};
|
||||||
|
|
||||||
export const formatLensText = (
|
export const formatLensText = (
|
||||||
{ make, model: modelRaw }: Lens,
|
{ make, model: modelRaw }: Lens,
|
||||||
length:
|
length:
|
||||||
'long' | // Unmodified make and model
|
'long' | // Unmodified make and model
|
||||||
'medium' | // Make and model, with modifiers removed
|
'medium' | // Make and model, with modifiers removed
|
||||||
'short' // Model only
|
'short' // Model only
|
||||||
= 'medium',
|
= 'short',
|
||||||
) => {
|
) => {
|
||||||
// Capture simple make without modifiers like 'Corporation' or 'Company'
|
// Capture simple make without modifiers like 'Corporation' or 'Company'
|
||||||
const makeSimple = make.match(/^(\S+)/)?.[1];
|
const makeSimple = make.match(/^(\S+)/)?.[1];
|
||||||
@ -69,7 +76,11 @@ export const formatLensText = (
|
|||||||
makeSimple &&
|
makeSimple &&
|
||||||
modelRaw.toLocaleLowerCase().startsWith(makeSimple.toLocaleLowerCase())
|
modelRaw.toLocaleLowerCase().startsWith(makeSimple.toLocaleLowerCase())
|
||||||
);
|
);
|
||||||
const model = modelRaw;
|
|
||||||
|
const model = isLensMakeApple(make)
|
||||||
|
? formatAppleLensText(modelRaw)
|
||||||
|
: modelRaw;
|
||||||
|
|
||||||
switch (length) {
|
switch (length) {
|
||||||
case 'long':
|
case 'long':
|
||||||
case 'medium':
|
case 'medium':
|
||||||
|
|||||||
@ -9,10 +9,11 @@ export const PHOTO_DEFAULT_LIMIT = 100;
|
|||||||
|
|
||||||
// Trim whitespace
|
// Trim whitespace
|
||||||
// Make lowercase
|
// Make lowercase
|
||||||
// Replace spaces with dashes
|
// Remove commas
|
||||||
// Remove periods and commas
|
// Replace spaces, slashes with dashes
|
||||||
const parameterizeForDb = (text: string) =>
|
const parameterizeForDb = (field: string) =>
|
||||||
`REPLACE(REPLACE(REPLACE(LOWER(TRIM(${text})), '.', ''), ',', ''), ' ', '-')`;
|
// eslint-disable-next-line max-len
|
||||||
|
`REPLACE(REPLACE(REPLACE(LOWER(TRIM(${field})), ',', ''), '/', '-'), ' ', '-')`;
|
||||||
|
|
||||||
export type GetPhotosOptions = {
|
export type GetPhotosOptions = {
|
||||||
sortBy?: 'createdAt' | 'createdAtAsc' | 'takenAt' | 'priority'
|
sortBy?: 'createdAt' | 'createdAtAsc' | 'takenAt' | 'priority'
|
||||||
@ -64,6 +65,19 @@ export const getWheresFromOptions = (
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.lens?.make) {
|
||||||
|
console.log('LENS MODEL QUERY',{
|
||||||
|
db: parameterizeForDb('lens_make'),
|
||||||
|
args: parameterize(options.lens.make),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (options.lens?.model) {
|
||||||
|
console.log('LENS MODEL QUERY',{
|
||||||
|
db: parameterizeForDb('lens_model'),
|
||||||
|
args: parameterize(options.lens.model),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (takenBefore) {
|
if (takenBefore) {
|
||||||
wheres.push(`taken_at < $${valuesIndex++}`);
|
wheres.push(`taken_at < $${valuesIndex++}`);
|
||||||
wheresValues.push(takenBefore.toISOString());
|
wheresValues.push(takenBefore.toISOString());
|
||||||
@ -87,19 +101,19 @@ export const getWheresFromOptions = (
|
|||||||
}
|
}
|
||||||
if (camera?.make) {
|
if (camera?.make) {
|
||||||
wheres.push(`${parameterizeForDb('make')}=$${valuesIndex++}`);
|
wheres.push(`${parameterizeForDb('make')}=$${valuesIndex++}`);
|
||||||
wheresValues.push(parameterize(camera.make, true));
|
wheresValues.push(parameterize(camera.make));
|
||||||
}
|
}
|
||||||
if (camera?.model) {
|
if (camera?.model) {
|
||||||
wheres.push(`${parameterizeForDb('model')}=$${valuesIndex++}`);
|
wheres.push(`${parameterizeForDb('model')}=$${valuesIndex++}`);
|
||||||
wheresValues.push(parameterize(camera.model, true));
|
wheresValues.push(parameterize(camera.model));
|
||||||
}
|
}
|
||||||
if (lens?.make) {
|
if (lens?.make) {
|
||||||
wheres.push(`${parameterizeForDb('lens_make')}=$${valuesIndex++}`);
|
wheres.push(`${parameterizeForDb('lens_make')}=$${valuesIndex++}`);
|
||||||
wheresValues.push(parameterize(lens.make, true));
|
wheresValues.push(parameterize(lens.make));
|
||||||
}
|
}
|
||||||
if (lens?.model) {
|
if (lens?.model) {
|
||||||
wheres.push(`${parameterizeForDb('lens_model')}=$${valuesIndex++}`);
|
wheres.push(`${parameterizeForDb('lens_model')}=$${valuesIndex++}`);
|
||||||
wheresValues.push(parameterize(lens.model, true));
|
wheresValues.push(parameterize(lens.model));
|
||||||
}
|
}
|
||||||
if (tag) {
|
if (tag) {
|
||||||
wheres.push(`$${valuesIndex++}=ANY(tags)`);
|
wheres.push(`$${valuesIndex++}=ANY(tags)`);
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { TAG_HIDDEN } from '.';
|
import { TAG_HIDDEN } from '.';
|
||||||
import { pathForTag } from '@/app/paths';
|
import { pathForTag } from '@/app/paths';
|
||||||
|
import IconHidden from '@/components/icons/IconHidden';
|
||||||
import EntityLink, {
|
import EntityLink, {
|
||||||
EntityLinkExternalProps,
|
EntityLinkExternalProps,
|
||||||
} from '@/components/primitives/EntityLink';
|
} from '@/components/primitives/EntityLink';
|
||||||
import { AiOutlineEyeInvisible } from 'react-icons/ai';
|
|
||||||
|
|
||||||
export default function HiddenTag({
|
export default function HiddenTag({
|
||||||
type,
|
type,
|
||||||
@ -20,14 +20,14 @@ export default function HiddenTag({
|
|||||||
label={badged
|
label={badged
|
||||||
? <span className="inline-flex items-center gap-1">
|
? <span className="inline-flex items-center gap-1">
|
||||||
{TAG_HIDDEN}
|
{TAG_HIDDEN}
|
||||||
<AiOutlineEyeInvisible
|
<IconHidden
|
||||||
size={13}
|
size={13}
|
||||||
className="translate-y-[-0.5px]"
|
className="translate-y-[-0.5px]"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
: TAG_HIDDEN}
|
: TAG_HIDDEN}
|
||||||
href={pathForTag(TAG_HIDDEN)}
|
href={pathForTag(TAG_HIDDEN)}
|
||||||
icon={!badged && <AiOutlineEyeInvisible size={16} />}
|
icon={!badged && <IconHidden size={16} />}
|
||||||
type={type}
|
type={type}
|
||||||
className={className}
|
className={className}
|
||||||
hoverEntity={countOnHover}
|
hoverEntity={countOnHover}
|
||||||
|
|||||||
@ -22,10 +22,10 @@ export const parameterize = (
|
|||||||
) =>
|
) =>
|
||||||
string
|
string
|
||||||
.trim()
|
.trim()
|
||||||
// Replaces spaces, underscores, and dashes with dashes
|
// Replaces spaces, underscores, slashes,and dashes with dashes
|
||||||
.replaceAll(/[\s_–—]/gi, '-')
|
.replaceAll(/[\s_–—/]/gi, '-')
|
||||||
// Removes punctuation
|
// Removes punctuation
|
||||||
.replaceAll(/['"!@#$%^&*()_+=[\]{};:/?,.<>\\|`~]/gi, '')
|
.replaceAll(/['"!@#$%^&*()_+=[\]{};:/?,<>\\|`~]/gi, '')
|
||||||
// Removes all non-alphanumeric characters
|
// Removes all non-alphanumeric characters
|
||||||
.replaceAll(
|
.replaceAll(
|
||||||
shouldRemoveNonAlphanumeric
|
shouldRemoveNonAlphanumeric
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user