Add lenses to sidebar and cmdk
This commit is contained in:
parent
3b7ec5a6c8
commit
dc765ae4e7
@ -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
|
||||
? <PhotoGridPage
|
||||
{...{ photos, photosCount, tags, cameras, simulations, recipes }}
|
||||
{...{
|
||||
photos,
|
||||
photosCount,
|
||||
cameras,
|
||||
lenses,
|
||||
tags,
|
||||
simulations,
|
||||
recipes,
|
||||
}}
|
||||
/>
|
||||
: <PhotosEmptyState />
|
||||
);
|
||||
|
||||
19
app/page.tsx
19
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
|
||||
? <PhotoGridPage
|
||||
{...{ photos, photosCount, tags, cameras, simulations, recipes }}
|
||||
/>
|
||||
: <PhotoFeedPage
|
||||
{...{ photos, photosCount }}
|
||||
{...{
|
||||
photos,
|
||||
photosCount,
|
||||
cameras,
|
||||
lenses,
|
||||
tags,
|
||||
simulations,
|
||||
recipes,
|
||||
}}
|
||||
/>
|
||||
: <PhotoFeedPage {...{ photos, photosCount }} />
|
||||
: <PhotosEmptyState />
|
||||
);
|
||||
}
|
||||
|
||||
@ -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 <CommandKClient
|
||||
tags={tags}
|
||||
cameras={cameras}
|
||||
lenses={lenses}
|
||||
tags={tags}
|
||||
simulations={filmSimulations}
|
||||
recipes={recipes}
|
||||
focalLengths={focalLengths}
|
||||
|
||||
@ -26,6 +26,7 @@ import {
|
||||
pathForCamera,
|
||||
pathForFilmSimulation,
|
||||
pathForFocalLength,
|
||||
pathForLens,
|
||||
pathForPhoto,
|
||||
pathForRecipe,
|
||||
pathForTag,
|
||||
@ -44,13 +45,11 @@ import { RiToolsFill } from 'react-icons/ri';
|
||||
import { BiLockAlt, BiSolidUser } from 'react-icons/bi';
|
||||
import { HiDocumentText } from 'react-icons/hi';
|
||||
import { signOutAction } from '@/auth/actions';
|
||||
import { TbChecklist, TbCone, TbPhoto } from 'react-icons/tb';
|
||||
import { getKeywordsForPhoto, titleForPhoto } from '@/photo';
|
||||
import PhotoDate from '@/photo/PhotoDate';
|
||||
import PhotoSmall from '@/photo/PhotoSmall';
|
||||
import { FaCheck } from 'react-icons/fa6';
|
||||
import { addHiddenToTags, formatTag } from '@/tag';
|
||||
import { FaTag } from 'react-icons/fa';
|
||||
import { formatCount, formatCountDescriptive } from '@/utility/string';
|
||||
import CommandKItem from './CommandKItem';
|
||||
import { CATEGORY_VISIBILITY, GRID_HOMEPAGE_ENABLED } from '@/app/config';
|
||||
@ -59,11 +58,17 @@ import * as VisuallyHidden from '@radix-ui/react-visually-hidden';
|
||||
import InsightsIndicatorDot from '@/admin/insights/InsightsIndicatorDot';
|
||||
import { PhotoSetCategories } from '@/photo/set';
|
||||
import { formatCameraText } from '@/camera';
|
||||
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';
|
||||
import IconLens from '../icons/IconLens';
|
||||
import { formatLensText } from '@/lens';
|
||||
import IconTag from '../icons/IconTag';
|
||||
import IconCamera from '../icons/IconCamera';
|
||||
import IconPhoto from '../icons/IconPhoto';
|
||||
import IconRecipe from '../icons/IconRecipe';
|
||||
import IconFocalLength from '../icons/IconFocalLength';
|
||||
import IconFilmSimulation from '../icons/IconFilmSimulation';
|
||||
|
||||
const DIALOG_TITLE = 'Global Command-K Menu';
|
||||
const DIALOG_DESCRIPTION = 'For searching photos, views, and settings';
|
||||
@ -98,8 +103,9 @@ const renderToggle = (
|
||||
});
|
||||
|
||||
export default function CommandKClient({
|
||||
tags,
|
||||
cameras,
|
||||
lenses,
|
||||
tags,
|
||||
recipes,
|
||||
simulations,
|
||||
focalLengths,
|
||||
@ -198,7 +204,7 @@ export default function CommandKClient({
|
||||
setQueriedSections(photos.length > 0
|
||||
? [{
|
||||
heading: 'Photos',
|
||||
accessory: <TbPhoto size={14} />,
|
||||
accessory: <IconPhoto size={14} />,
|
||||
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: <IconCamera size={14} />,
|
||||
items: cameras.map(({ camera, count }) => ({
|
||||
label: formatCameraText(camera),
|
||||
annotation: formatCount(count),
|
||||
annotationAria: formatCountDescriptive(count),
|
||||
path: pathForCamera(camera),
|
||||
})),
|
||||
};
|
||||
case 'lenses': return {
|
||||
heading: 'Lenses',
|
||||
accessory: <IconLens size={14} className="translate-y-[0.5px]" />,
|
||||
items: lenses.map(({ lens, count }) => ({
|
||||
label: formatLensText(lens, 'medium'),
|
||||
annotation: formatCount(count),
|
||||
annotationAria: formatCountDescriptive(count),
|
||||
path: pathForLens(lens),
|
||||
})),
|
||||
};
|
||||
case 'tags': return {
|
||||
heading: 'Tags',
|
||||
accessory: <FaTag
|
||||
size={10}
|
||||
accessory: <IconTag
|
||||
size={13}
|
||||
className="translate-x-[1px] translate-y-[0.75px]"
|
||||
/>,
|
||||
items: tagsIncludingHidden.map(({ tag, count }) => ({
|
||||
@ -263,19 +289,9 @@ export default function CommandKClient({
|
||||
path: pathForTag(tag),
|
||||
})),
|
||||
};
|
||||
case 'cameras': return {
|
||||
heading: 'Cameras',
|
||||
accessory: <IoMdCamera />,
|
||||
items: cameras.map(({ camera, count }) => ({
|
||||
label: formatCameraText(camera),
|
||||
annotation: formatCount(count),
|
||||
annotationAria: formatCountDescriptive(count),
|
||||
path: pathForCamera(camera),
|
||||
})),
|
||||
};
|
||||
case 'recipes': return {
|
||||
heading: 'Recipes',
|
||||
accessory: <TbChecklist
|
||||
accessory: <IconRecipe
|
||||
size={15}
|
||||
className="translate-x-[-1px]"
|
||||
/>,
|
||||
@ -288,9 +304,7 @@ export default function CommandKClient({
|
||||
};
|
||||
case 'films': return {
|
||||
heading: 'Film Simulations',
|
||||
accessory: <span className="w-3">
|
||||
<PhotoFilmSimulationIcon className="translate-y-[0.5px]" />
|
||||
</span>,
|
||||
accessory: <IconFilmSimulation size={14} />,
|
||||
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: <TbCone
|
||||
className="rotate-[270deg] text-[14px]"
|
||||
/>,
|
||||
accessory: <IconFocalLength className="text-[14px]" />,
|
||||
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',
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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({
|
||||
<PhotoGridSidebar {...{
|
||||
tags,
|
||||
cameras,
|
||||
lenses,
|
||||
simulations,
|
||||
recipes,
|
||||
photosCount,
|
||||
|
||||
@ -24,16 +24,21 @@ import IconCamera from '@/components/icons/IconCamera';
|
||||
import IconRecipe from '@/components/icons/IconRecipe';
|
||||
import IconTag from '@/components/icons/IconTag';
|
||||
import IconFilmSimulation from '@/components/icons/IconFilmSimulation';
|
||||
import IconLens from '@/components/icons/IconLens';
|
||||
import { Lenses, sortLensesWithCount } from '@/lens';
|
||||
import PhotoLens from '@/lens/PhotoLens';
|
||||
|
||||
export default function PhotoGridSidebar({
|
||||
tags,
|
||||
cameras,
|
||||
lenses,
|
||||
tags,
|
||||
simulations,
|
||||
recipes,
|
||||
photosCount,
|
||||
photosDateRange,
|
||||
}: {
|
||||
tags: Tags
|
||||
lenses: Lenses
|
||||
cameras: Cameras
|
||||
simulations: FilmSimulations
|
||||
recipes: Recipes
|
||||
@ -48,6 +53,47 @@ export default function PhotoGridSidebar({
|
||||
addHiddenToTags(tags, photosCountHidden)
|
||||
, [tags, photosCountHidden]);
|
||||
|
||||
const camerasContent = cameras.length > 0
|
||||
? <HeaderList
|
||||
key="cameras"
|
||||
title="Cameras"
|
||||
icon={<IconCamera size={15} />}
|
||||
items={cameras
|
||||
.sort(sortCamerasWithCount)
|
||||
.map(({ cameraKey, camera, count }) =>
|
||||
<PhotoCamera
|
||||
key={cameraKey}
|
||||
camera={camera}
|
||||
type="text-only"
|
||||
countOnHover={count}
|
||||
prefetch={false}
|
||||
contrast="low"
|
||||
hideAppleIcon
|
||||
badged
|
||||
/>)}
|
||||
/>
|
||||
: null;
|
||||
|
||||
const lensesContent = lenses.length > 0
|
||||
? <HeaderList
|
||||
key="lenses"
|
||||
title="Lenses"
|
||||
icon={<IconLens size={15} />}
|
||||
items={lenses
|
||||
.sort(sortLensesWithCount)
|
||||
.map(({ lensKey, lens, count }) =>
|
||||
<PhotoLens
|
||||
key={lensKey}
|
||||
lens={lens}
|
||||
type="text-only"
|
||||
countOnHover={count}
|
||||
prefetch={false}
|
||||
contrast="low"
|
||||
badged
|
||||
/>)}
|
||||
/>
|
||||
: null;
|
||||
|
||||
const tagsContent = tags.length > 0
|
||||
? <HeaderList
|
||||
key="tags"
|
||||
@ -91,27 +137,6 @@ export default function PhotoGridSidebar({
|
||||
/>
|
||||
: null;
|
||||
|
||||
const camerasContent = cameras.length > 0
|
||||
? <HeaderList
|
||||
key="cameras"
|
||||
title="Cameras"
|
||||
icon={<IconCamera size={15} />}
|
||||
items={cameras
|
||||
.sort(sortCamerasWithCount)
|
||||
.map(({ cameraKey, camera, count }) =>
|
||||
<PhotoCamera
|
||||
key={cameraKey}
|
||||
camera={camera}
|
||||
type="text-only"
|
||||
countOnHover={count}
|
||||
prefetch={false}
|
||||
contrast="low"
|
||||
hideAppleIcon
|
||||
badged
|
||||
/>)}
|
||||
/>
|
||||
: null;
|
||||
|
||||
const recipesContent = recipes.length > 0
|
||||
? <HeaderList
|
||||
key="recipes"
|
||||
@ -183,8 +208,9 @@ export default function PhotoGridSidebar({
|
||||
/>}
|
||||
{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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user