Offer config for showing only tags with multiple photos

This commit is contained in:
Sam Becker 2025-06-25 09:29:05 -05:00
parent c73c36951d
commit 21d465c1ec
5 changed files with 49 additions and 14 deletions

View File

@ -128,12 +128,7 @@ Application behavior can be changed by configuring the following environment var
- `NEXT_PUBLIC_IMAGE_QUALITY = 1-100` controls the quality of large photos - `NEXT_PUBLIC_IMAGE_QUALITY = 1-100` controls the quality of large photos
- `NEXT_PUBLIC_BLUR_DISABLED = 1` prevents image blur data being stored and displayed (potentially useful for limiting Postgres usage) - `NEXT_PUBLIC_BLUR_DISABLED = 1` prevents image blur data being stored and displayed (potentially useful for limiting Postgres usage)
#### Visual #### Categories
- `NEXT_PUBLIC_DEFAULT_THEME = light | dark` sets preferred initial theme (defaults to `system` when not configured)
- `NEXT_PUBLIC_MATTE_PHOTOS = 1` constrains the size of each photo, and displays a surrounding border, potentially useful for photos with tall aspect ratios (colors can be customized via `NEXT_PUBLIC_MATTE_COLOR` + `NEXT_PUBLIC_MATTE_COLOR_DARK`)
#### Display
- `NEXT_PUBLIC_CATEGORY_VISIBILITY` - `NEXT_PUBLIC_CATEGORY_VISIBILITY`
- Comma-separated value controlling which photo sets appear in grid sidebar and CMD-K menu, and in what order. For example, you could move cameras above tags, and hide film simulations, by updating to `cameras,tags,lenses,recipes`. - Comma-separated value controlling which photo sets appear in grid sidebar and CMD-K menu, and in what order. For example, you could move cameras above tags, and hide film simulations, by updating to `cameras,tags,lenses,recipes`.
- Accepted values: - Accepted values:
@ -144,6 +139,9 @@ Application behavior can be changed by configuring the following environment var
- `films` (default) - `films` (default)
- `focal-lengths` - `focal-lengths`
- `NEXT_PUBLIC_EXHAUSTIVE_SIDEBAR_CATEGORIES = 1` always shows expanded sidebar content - `NEXT_PUBLIC_EXHAUSTIVE_SIDEBAR_CATEGORIES = 1` always shows expanded sidebar content
- `NEXT_PUBLIC_HIDE_TAGS_WITH_ONE_PHOTO = 1` to only show tags with 2 or more photos
#### Display
- `NEXT_PUBLIC_HIDE_KEYBOARD_SHORTCUT_TOOLTIPS = 1` hides keyboard shortcut hints in areas like the main nav, and previous/next photo links - `NEXT_PUBLIC_HIDE_KEYBOARD_SHORTCUT_TOOLTIPS = 1` hides keyboard shortcut hints in areas like the main nav, and previous/next photo links
- `NEXT_PUBLIC_HIDE_EXIF_DATA = 1` hides EXIF data in photo details and OG images (potentially useful for portfolios, which don't focus on photography) - `NEXT_PUBLIC_HIDE_EXIF_DATA = 1` hides EXIF data in photo details and OG images (potentially useful for portfolios, which don't focus on photography)
- `NEXT_PUBLIC_CATEGORY_IMAGE_HOVERS = 1` shows images when hovering over category links like cameras and lenses (⚠️ setting `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORY_OG_IMAGES = 1` strongly recommended for responsive hover interactions) - `NEXT_PUBLIC_CATEGORY_IMAGE_HOVERS = 1` shows images when hovering over category links like cameras and lenses (⚠️ setting `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORY_OG_IMAGES = 1` strongly recommended for responsive hover interactions)
@ -157,6 +155,10 @@ Application behavior can be changed by configuring the following environment var
- `NEXT_PUBLIC_GRID_ASPECT_RATIO = 1.5` sets aspect ratio for grid tiles (defaults to `1`—setting to `0` removes the constraint) - `NEXT_PUBLIC_GRID_ASPECT_RATIO = 1.5` sets aspect ratio for grid tiles (defaults to `1`—setting to `0` removes the constraint)
- `NEXT_PUBLIC_SHOW_LARGE_THUMBNAILS = 1` ensures large thumbnails on photo grid views (if not configured, density is based on aspect ratio) - `NEXT_PUBLIC_SHOW_LARGE_THUMBNAILS = 1` ensures large thumbnails on photo grid views (if not configured, density is based on aspect ratio)
#### Design
- `NEXT_PUBLIC_DEFAULT_THEME = light | dark` sets preferred initial theme (defaults to `system` when not configured)
- `NEXT_PUBLIC_MATTE_PHOTOS = 1` constrains the size of each photo, and displays a surrounding border, potentially useful for photos with tall aspect ratios (colors can be customized via `NEXT_PUBLIC_MATTE_COLOR` + `NEXT_PUBLIC_MATTE_COLOR_DARK`)
#### Settings #### Settings
- `NEXT_PUBLIC_GEO_PRIVACY = 1` disables collection/display of location-based data (⚠️ re-compresses uploaded images in order to remove GPS information) - `NEXT_PUBLIC_GEO_PRIVACY = 1` disables collection/display of location-based data (⚠️ re-compresses uploaded images in order to remove GPS information)
- `NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS = 1` enables public photo downloads for all visitors (⚠️ may result in increased bandwidth usage) - `NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS = 1` enables public photo downloads for all visitors (⚠️ may result in increased bandwidth usage)

View File

@ -80,10 +80,12 @@ export default function AdminAppConfigurationClient({
hasImageQuality, hasImageQuality,
imageQuality, imageQuality,
isBlurEnabled, isBlurEnabled,
// Display // Categories
categoryVisibility, categoryVisibility,
hasCategoryVisibility, hasCategoryVisibility,
collapseSidebarCategories, collapseSidebarCategories,
hideTagsWithOnePhoto,
// Display
showKeyboardShortcutTooltips, showKeyboardShortcutTooltips,
showExifInfo, showExifInfo,
showCategoryImageHover, showCategoryImageHover,
@ -401,7 +403,7 @@ export default function AdminAppConfigurationClient({
<ChecklistGroup <ChecklistGroup
title="AI text generation" title="AI text generation"
titleShort="AI" titleShort="AI"
icon={<HiSparkles />} icon={<HiSparkles size={14} />}
optional optional
> >
<ChecklistRow <ChecklistRow
@ -470,7 +472,7 @@ export default function AdminAppConfigurationClient({
</ChecklistGroup> </ChecklistGroup>
<ChecklistGroup <ChecklistGroup
title="Performance" title="Performance"
icon={<RiSpeedMiniLine size={18} />} icon={<RiSpeedMiniLine size={19} />}
optional optional
> >
<ChecklistRow <ChecklistRow
@ -578,6 +580,15 @@ export default function AdminAppConfigurationClient({
expanded category content expanded category content
{renderEnvVars(['NEXT_PUBLIC_EXHAUSTIVE_SIDEBAR_CATEGORIES'])} {renderEnvVars(['NEXT_PUBLIC_EXHAUSTIVE_SIDEBAR_CATEGORIES'])}
</ChecklistRow> </ChecklistRow>
<ChecklistRow
title="Hide tags with only 1 photo"
status={hideTagsWithOnePhoto}
optional
>
Set environment variable to {'"1"'} to only show tags
with 2 or more photos
{renderEnvVars(['NEXT_PUBLIC_HIDE_TAGS_WITH_ONE_PHOTO'])}
</ChecklistRow>
</ChecklistGroup> </ChecklistGroup>
<ChecklistGroup <ChecklistGroup
title="Display" title="Display"

View File

@ -240,7 +240,7 @@ export const IMAGE_QUALITY =
export const BLUR_ENABLED = export const BLUR_ENABLED =
process.env.NEXT_PUBLIC_BLUR_DISABLED !== '1'; process.env.NEXT_PUBLIC_BLUR_DISABLED !== '1';
// DISPLAY // CATEGORIES
export const CATEGORY_VISIBILITY = getOrderedCategoriesFromString( export const CATEGORY_VISIBILITY = getOrderedCategoriesFromString(
process.env.NEXT_PUBLIC_CATEGORY_VISIBILITY); process.env.NEXT_PUBLIC_CATEGORY_VISIBILITY);
@ -258,6 +258,11 @@ export const SHOW_FOCAL_LENGTHS =
CATEGORY_VISIBILITY.includes('focal-lengths'); CATEGORY_VISIBILITY.includes('focal-lengths');
export const COLLAPSE_SIDEBAR_CATEGORIES = export const COLLAPSE_SIDEBAR_CATEGORIES =
process.env.NEXT_PUBLIC_EXHAUSTIVE_SIDEBAR_CATEGORIES !== '1'; process.env.NEXT_PUBLIC_EXHAUSTIVE_SIDEBAR_CATEGORIES !== '1';
export const HIDE_TAGS_WITH_ONE_PHOTO =
process.env.NEXT_PUBLIC_HIDE_TAGS_WITH_ONE_PHOTO === '1';
// DISPLAY
export const SHOW_KEYBOARD_SHORTCUT_TOOLTIPS = export const SHOW_KEYBOARD_SHORTCUT_TOOLTIPS =
process.env.NEXT_PUBLIC_HIDE_KEYBOARD_SHORTCUT_TOOLTIPS !== '1'; process.env.NEXT_PUBLIC_HIDE_KEYBOARD_SHORTCUT_TOOLTIPS !== '1';
export const SHOW_EXIF_DATA = export const SHOW_EXIF_DATA =
@ -388,11 +393,13 @@ export const APP_CONFIGURATION = {
hasImageQuality: Boolean(process.env.NEXT_PUBLIC_IMAGE_QUALITY), hasImageQuality: Boolean(process.env.NEXT_PUBLIC_IMAGE_QUALITY),
imageQuality: IMAGE_QUALITY, imageQuality: IMAGE_QUALITY,
isBlurEnabled: BLUR_ENABLED, isBlurEnabled: BLUR_ENABLED,
// Display // Categories
hasCategoryVisibility: hasCategoryVisibility:
Boolean(process.env.NEXT_PUBLIC_CATEGORY_VISIBILITY), Boolean(process.env.NEXT_PUBLIC_CATEGORY_VISIBILITY),
categoryVisibility: CATEGORY_VISIBILITY, categoryVisibility: CATEGORY_VISIBILITY,
collapseSidebarCategories: COLLAPSE_SIDEBAR_CATEGORIES, collapseSidebarCategories: COLLAPSE_SIDEBAR_CATEGORIES,
hideTagsWithOnePhoto: HIDE_TAGS_WITH_ONE_PHOTO,
// Display
showKeyboardShortcutTooltips: SHOW_KEYBOARD_SHORTCUT_TOOLTIPS, showKeyboardShortcutTooltips: SHOW_KEYBOARD_SHORTCUT_TOOLTIPS,
showExifInfo: SHOW_EXIF_DATA, showExifInfo: SHOW_EXIF_DATA,
showCategoryImageHover: SHOW_CATEGORY_IMAGE_HOVERS, showCategoryImageHover: SHOW_CATEGORY_IMAGE_HOVERS,

View File

@ -4,13 +4,13 @@ import PhotoCamera from '@/camera/PhotoCamera';
import HeaderList from '@/components/HeaderList'; import HeaderList from '@/components/HeaderList';
import PhotoTag from '@/tag/PhotoTag'; import PhotoTag from '@/tag/PhotoTag';
import { PhotoDateRange, dateRangeForPhotos, photoQuantityText } from '.'; import { PhotoDateRange, dateRangeForPhotos, photoQuantityText } from '.';
import { TAG_FAVS, TAG_HIDDEN, addHiddenToTags } from '@/tag'; import { TAG_FAVS, TAG_HIDDEN, addHiddenToTags, limitTagsByCount } from '@/tag';
import PhotoFilm from '@/film/PhotoFilm'; import PhotoFilm from '@/film/PhotoFilm';
import FavsTag from '../tag/FavsTag'; import FavsTag from '../tag/FavsTag';
import { useAppState } from '@/state/AppState'; import { useAppState } from '@/state/AppState';
import { useMemo, useRef } from 'react'; import { useMemo, useRef } from 'react';
import HiddenTag from '@/tag/HiddenTag'; import HiddenTag from '@/tag/HiddenTag';
import { CATEGORY_VISIBILITY } from '@/app/config'; import { CATEGORY_VISIBILITY, HIDE_TAGS_WITH_ONE_PHOTO } from '@/app/config';
import { clsx } from 'clsx/lite'; import { clsx } from 'clsx/lite';
import PhotoRecipe from '@/recipe/PhotoRecipe'; import PhotoRecipe from '@/recipe/PhotoRecipe';
import IconCamera from '@/components/icons/IconCamera'; import IconCamera from '@/components/icons/IconCamera';
@ -37,7 +37,7 @@ export default function PhotoGridSidebar({
containerHeight, containerHeight,
aboutTextSafelyParsedHtml, aboutTextSafelyParsedHtml,
aboutTextHasBrParagraphBreaks, aboutTextHasBrParagraphBreaks,
...categories ..._categories
}: PhotoSetCategories & { }: PhotoSetCategories & {
photosCount: number photosCount: number
photosDateRange?: PhotoDateRange photosDateRange?: PhotoDateRange
@ -45,6 +45,14 @@ export default function PhotoGridSidebar({
aboutTextSafelyParsedHtml?: string aboutTextSafelyParsedHtml?: string
aboutTextHasBrParagraphBreaks?: boolean aboutTextHasBrParagraphBreaks?: boolean
}) { }) {
const categories = useMemo(() => HIDE_TAGS_WITH_ONE_PHOTO
? {
..._categories,
tags: limitTagsByCount(_categories.tags, 2),
}
: _categories
, [_categories]);
const { const {
cameras, cameras,
lenses, lenses,

View File

@ -167,3 +167,10 @@ export const convertTagsForForm = (
annotationAria: annotationAria:
formatCountDescriptive(count, appText.category.taggedPhotos), formatCountDescriptive(count, appText.category.taggedPhotos),
})); }));
export const limitTagsByCount = (tags: Tags, minimumCount: number) =>
tags.filter(({ tag, count }) => (
count >= minimumCount ||
isTagFavs(tag) ||
isTagHidden(tag)
));