diff --git a/app/grid/page.tsx b/app/grid/page.tsx
index e3b932bf..3866e733 100644
--- a/app/grid/page.tsx
+++ b/app/grid/page.tsx
@@ -25,8 +25,9 @@ export default async function GridPage() {
const [
photos,
photosCount,
- tags,
cameras,
+ lenses,
+ tags,
simulations,
recipes,
] = await Promise.all([
@@ -41,7 +42,15 @@ export default async function GridPage() {
return (
photos.length > 0
?
:
);
diff --git a/app/page.tsx b/app/page.tsx
index 69045482..6568c609 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -31,8 +31,9 @@ export default async function HomePage() {
const [
photos,
photosCount,
- tags,
cameras,
+ lenses,
+ tags,
simulations,
recipes,
] = await Promise.all([
@@ -43,18 +44,24 @@ export default async function HomePage() {
.catch(() => 0),
...(GRID_HOMEPAGE_ENABLED
? getPhotoSidebarData()
- : [[], [], [], []]),
+ : [[], [], [], [], []]),
]);
return (
photos.length > 0
? GRID_HOMEPAGE_ENABLED
?
- :
+ :
:
);
}
diff --git a/src/app/CommandK.tsx b/src/app/CommandK.tsx
index 341765a5..a32066b0 100644
--- a/src/app/CommandK.tsx
+++ b/src/app/CommandK.tsx
@@ -3,6 +3,7 @@ import {
getPhotosMetaCached,
getUniqueCamerasCached,
getUniqueFilmSimulationsCached,
+ getUniqueLensesCached,
getUniqueRecipesCached,
getUniqueTagsCached,
} from '@/photo/cache';
@@ -17,8 +18,9 @@ import { getUniqueFocalLengths } from '@/photo/db/query';
export default async function CommandK() {
const [
count,
- tags,
cameras,
+ lenses,
+ tags,
recipes,
filmSimulations,
focalLengths,
@@ -26,8 +28,9 @@ export default async function CommandK() {
getPhotosMetaCached()
.then(({ count }) => count)
.catch(() => 0),
- getUniqueTagsCached().catch(() => []),
getUniqueCamerasCached().catch(() => []),
+ getUniqueLensesCached().catch(() => []),
+ getUniqueTagsCached().catch(() => []),
SHOW_RECIPES
? getUniqueRecipesCached().catch(() => [])
: [],
@@ -38,8 +41,9 @@ export default async function CommandK() {
]);
return 0
? [{
heading: 'Photos',
- accessory: ,
+ accessory: ,
items: photos.map(photo => ({
label: titleForPhoto(photo),
keywords: getKeywordsForPhoto(photo),
@@ -250,10 +256,30 @@ export default function CommandKClient({
CATEGORY_VISIBILITY
.map(category => {
switch (category) {
+ case 'cameras': return {
+ heading: 'Cameras',
+ accessory: ,
+ items: cameras.map(({ camera, count }) => ({
+ label: formatCameraText(camera),
+ annotation: formatCount(count),
+ annotationAria: formatCountDescriptive(count),
+ path: pathForCamera(camera),
+ })),
+ };
+ case 'lenses': return {
+ heading: 'Lenses',
+ accessory: ,
+ items: lenses.map(({ lens, count }) => ({
+ label: formatLensText(lens, 'medium'),
+ annotation: formatCount(count),
+ annotationAria: formatCountDescriptive(count),
+ path: pathForLens(lens),
+ })),
+ };
case 'tags': return {
heading: 'Tags',
- accessory: ,
items: tagsIncludingHidden.map(({ tag, count }) => ({
@@ -263,19 +289,9 @@ export default function CommandKClient({
path: pathForTag(tag),
})),
};
- case 'cameras': return {
- heading: 'Cameras',
- accessory: ,
- items: cameras.map(({ camera, count }) => ({
- label: formatCameraText(camera),
- annotation: formatCount(count),
- annotationAria: formatCountDescriptive(count),
- path: pathForCamera(camera),
- })),
- };
case 'recipes': return {
heading: 'Recipes',
- accessory: ,
@@ -288,9 +304,7 @@ export default function CommandKClient({
};
case 'films': return {
heading: 'Film Simulations',
- accessory:
-
- ,
+ accessory: ,
items: simulations.map(({ simulation, count }) => ({
label: labelForFilmSimulation(simulation).medium,
annotation: formatCount(count),
@@ -300,9 +314,7 @@ export default function CommandKClient({
};
case 'focal-lengths': return {
heading: 'Focal Lengths',
- accessory: ,
+ accessory: ,
items: focalLengths.map(({ focal, count }) => ({
label: formatFocalLength(focal)!,
annotation: formatCount(count),
@@ -313,7 +325,7 @@ export default function CommandKClient({
}
})
.filter(Boolean) as CommandKSection[]
- , [tagsIncludingHidden, cameras, recipes, simulations, focalLengths]);
+ , [tagsIncludingHidden, cameras, lenses, recipes, simulations, focalLengths]);
const clientSections: CommandKSection[] = [{
heading: 'Theme',
diff --git a/src/lens/index.ts b/src/lens/index.ts
index e102c6ff..7aa7182f 100644
--- a/src/lens/index.ts
+++ b/src/lens/index.ts
@@ -41,6 +41,15 @@ export const getLensFromParams = ({
model: parameterize(model),
});
+export const sortLensesWithCount = (
+ a: LensWithCount,
+ b: LensWithCount,
+) => {
+ const aText = formatLensText(a.lens);
+ const bText = formatLensText(b.lens);
+ return aText.localeCompare(bText);
+};
+
export const lensFromPhoto = (
photo: Photo | undefined,
fallback?: Lens,
@@ -55,15 +64,24 @@ const isLensMakeApple = (make?: string) =>
export const isLensApple = ({ make }: Lens) =>
isLensMakeApple(make);
-const formatAppleLensText = (model: string) => {
- if (model.includes('front')) {
- return 'Front Camera';
- } else {
- if (model.includes('15 Pro')) {
- if (model.includes('f/2.2')) { return 'Wide Camera'; }
- if (model.includes('f/1.78')) { return 'Main Camera'; }
- if (model.includes('f/2.8')) { return 'Telephoto Camera'; }
- }
+const formatAppleLensText = (
+ model: string,
+ includePhoneName?: boolean,
+) => {
+ if (model.includes('15 Pro')) {
+ const phoneName = '15 Pro';
+ if (model.includes('front')) { return includePhoneName
+ ? `${phoneName}: Front Camera`
+ : 'Front Camera'; }
+ if (model.includes('f/2.2')) { return includePhoneName
+ ? `${phoneName}: Wide Camera`
+ : 'Wide Camera'; }
+ if (model.includes('f/1.78')) { return includePhoneName
+ ? `${phoneName}: Main Camera`
+ : 'Main Camera'; }
+ if (model.includes('f/2.8')) { return includePhoneName
+ ? `${phoneName}: Telephoto Camera`
+ : 'Telephoto Camera'; }
}
return model;
};
@@ -84,13 +102,16 @@ export const formatLensText = (
);
const model = isLensMakeApple(make)
- ? formatAppleLensText(modelRaw)
+ ? formatAppleLensText(modelRaw, length === 'medium')
: modelRaw;
switch (length) {
case 'long':
- case 'medium':
return `${make} ${model}`;
+ case 'medium':
+ return doesModelStartWithMake
+ ? model.replace(makeSimple, '').trim()
+ : model;
case 'short':
return doesModelStartWithMake
? model.replace(makeSimple, '').trim()
diff --git a/src/photo/PhotoGridPage.tsx b/src/photo/PhotoGridPage.tsx
index ff1966fc..4a3de748 100644
--- a/src/photo/PhotoGridPage.tsx
+++ b/src/photo/PhotoGridPage.tsx
@@ -11,19 +11,22 @@ import { useEffect } from 'react';
import { useAppState } from '@/state/AppState';
import clsx from 'clsx/lite';
import { Recipes } from '@/recipe';
+import { Lenses } from '@/lens';
export default function PhotoGridPage({
photos,
photosCount,
- tags,
cameras,
+ lenses,
+ tags,
simulations,
recipes,
}: {
photos: Photo[]
photosCount: number
- tags: Tags
cameras: Cameras
+ lenses: Lenses
+ tags: Tags
simulations: FilmSimulations
recipes: Recipes
}) {
@@ -65,6 +68,7 @@ export default function PhotoGridPage({
0
+ ? }
+ items={cameras
+ .sort(sortCamerasWithCount)
+ .map(({ cameraKey, camera, count }) =>
+ )}
+ />
+ : null;
+
+ const lensesContent = lenses.length > 0
+ ? }
+ items={lenses
+ .sort(sortLensesWithCount)
+ .map(({ lensKey, lens, count }) =>
+ )}
+ />
+ : null;
+
const tagsContent = tags.length > 0
?
: null;
- const camerasContent = cameras.length > 0
- ? }
- items={cameras
- .sort(sortCamerasWithCount)
- .map(({ cameraKey, camera, count }) =>
- )}
- />
- : null;
-
const recipesContent = recipes.length > 0
? }
{CATEGORY_VISIBILITY.map(category => {
switch (category) {
- case 'cameras': return camerasContent;
case 'tags': return tagsContent;
+ case 'cameras': return camerasContent;
+ case 'lenses': return lensesContent;
case 'recipes': return recipesContent;
case 'films': return filmsContent;
}
diff --git a/src/photo/data.ts b/src/photo/data.ts
index 986d9e91..caa15e20 100644
--- a/src/photo/data.ts
+++ b/src/photo/data.ts
@@ -1,21 +1,24 @@
import {
getUniqueCamerasCached,
getUniqueFilmSimulationsCached,
+ getUniqueLensesCached,
getUniqueRecipesCached,
getUniqueTagsCached,
} from '@/photo/cache';
import {
getUniqueCameras,
getUniqueFilmSimulations,
+ getUniqueLenses,
getUniqueRecipes,
getUniqueTags,
} from '@/photo/db/query';
-import { SHOW_FILM_SIMULATIONS, SHOW_RECIPES } from '@/app/config';
+import { SHOW_FILM_SIMULATIONS, SHOW_LENSES, SHOW_RECIPES } from '@/app/config';
import { sortTagsObject } from '@/tag';
export const getPhotoSidebarData = () => [
- getUniqueTags().then(sortTagsObject).catch(() => []),
getUniqueCameras().catch(() => []),
+ SHOW_LENSES ? getUniqueLenses().catch(() => []) : [],
+ getUniqueTags().then(sortTagsObject).catch(() => []),
SHOW_FILM_SIMULATIONS
? getUniqueFilmSimulations().catch(() => [])
: [],
@@ -25,8 +28,9 @@ export const getPhotoSidebarData = () => [
] as const;
export const getPhotoSidebarDataCached = () => [
- getUniqueTagsCached().then(sortTagsObject),
getUniqueCamerasCached(),
+ SHOW_LENSES ? getUniqueLensesCached() : [],
+ getUniqueTagsCached().then(sortTagsObject),
SHOW_FILM_SIMULATIONS ? getUniqueFilmSimulationsCached() : [],
SHOW_RECIPES ? getUniqueRecipesCached() : [],
] as const;
diff --git a/src/photo/set.ts b/src/photo/set.ts
index a27518e5..57081775 100644
--- a/src/photo/set.ts
+++ b/src/photo/set.ts
@@ -2,7 +2,7 @@ import { Photo } from '.';
import { Camera, Cameras } from '@/camera';
import { PhotoDateRange } from '.';
import { FilmSimulation, FilmSimulations } from '@/simulation';
-import { Lens } from '@/lens';
+import { Lens, Lenses } from '@/lens';
import { Tags } from '@/tag';
import { FocalLengths } from '@/focal';
import { Recipes } from '@/recipe';
@@ -43,8 +43,9 @@ export interface PhotoSetCategory {
}
export interface PhotoSetCategories {
- tags: Tags
cameras: Cameras
+ lenses: Lenses
+ tags: Tags
recipes: Recipes
simulations: FilmSimulations
focalLengths: FocalLengths