Offer sidebar ordering with paired insight
This commit is contained in:
parent
d52bc017cb
commit
74e91be001
@ -132,6 +132,7 @@ Application behavior can be changed by configuring the following environment var
|
||||
- `NEXT_PUBLIC_HIDE_FILM_SIMULATIONS = 1` prevents Fujifilm simulations showing up in `/grid` sidebar and CMD-K search results
|
||||
- `NEXT_PUBLIC_HIDE_RECIPES = 1` prevents Fujifilm recipe button showing up in photo meta
|
||||
- `NEXT_PUBLIC_HIDE_REPO_LINK = 1` removes footer link to repo
|
||||
- `NEXT_PUBLIC_CAMERAS_FIRST = 1` shows cameras above tags in grid sidebar
|
||||
|
||||
#### Grid
|
||||
- `NEXT_PUBLIC_GRID_HOMEPAGE = 1` shows grid layout on homepage
|
||||
@ -143,7 +144,6 @@ Application behavior can be changed by configuring the following environment var
|
||||
- `NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS = 1` enables public photo downloads for all visitors (⚠️ may result in increased bandwidth usage)
|
||||
- `NEXT_PUBLIC_PUBLIC_API = 1` enables public API available at `/api`
|
||||
- `NEXT_PUBLIC_IGNORE_PRIORITY_ORDER = 1` prevents `priority_order` field affecting photo order
|
||||
- `NEXT_PUBLIC_SHOW_LARGE_THUMBNAILS = 1` ensures large thumbnails on photo grid views
|
||||
- `NEXT_PUBLIC_OG_TEXT_ALIGNMENT = BOTTOM` keeps OG image text bottom aligned (default is top)
|
||||
|
||||
## Alternate storage providers
|
||||
|
||||
@ -75,6 +75,7 @@ export default function AdminAppConfigurationClient({
|
||||
showFilmSimulations,
|
||||
showRecipes,
|
||||
showRepoLink,
|
||||
showSidebarCamerasFirst,
|
||||
// Grid
|
||||
isGridHomepageEnabled,
|
||||
gridAspectRatio,
|
||||
@ -543,6 +544,15 @@ export default function AdminAppConfigurationClient({
|
||||
Set environment variable to {'"1"'} to hide footer link:
|
||||
{renderEnvVars(['NEXT_PUBLIC_HIDE_REPO_LINK'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title="Show cameras first"
|
||||
status={showSidebarCamerasFirst}
|
||||
optional
|
||||
>
|
||||
Set environment variable to {'"1"'} to show cameras
|
||||
above tags in grid sidebar:
|
||||
{renderEnvVars(['NEXT_PUBLIC_CAMERAS_FIRST'])}
|
||||
</ChecklistRow>
|
||||
</ChecklistGroup>
|
||||
<ChecklistGroup
|
||||
title="Grid"
|
||||
|
||||
@ -10,7 +10,7 @@ export default function AdminAppInfoIcon({
|
||||
size?: 'small' | 'large'
|
||||
className?: string
|
||||
}) {
|
||||
const { insightIndicatorStatus } = useAppState();
|
||||
const { insightsIndicatorStatus } = useAppState();
|
||||
|
||||
return (
|
||||
<span className={clsx(
|
||||
@ -22,7 +22,7 @@ export default function AdminAppInfoIcon({
|
||||
className="inline-flex translate-y-[1px]"
|
||||
aria-label="App Info"
|
||||
/>
|
||||
{insightIndicatorStatus &&
|
||||
{insightsIndicatorStatus &&
|
||||
<InsightsIndicatorDot
|
||||
size={size}
|
||||
top={size === 'large' ? 1.5 : 1.5}
|
||||
|
||||
@ -37,7 +37,7 @@ export default function AdminInfoPage({
|
||||
|
||||
const hasMultiplePages = pages.length > 1;
|
||||
|
||||
const { insightIndicatorStatus } = useAppState();
|
||||
const { insightsIndicatorStatus } = useAppState();
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-4 min-h-9">
|
||||
@ -65,7 +65,7 @@ export default function AdminInfoPage({
|
||||
<ResponsiveText shortText={titleShort}>
|
||||
{title}
|
||||
</ResponsiveText>
|
||||
{title === 'App Insights' && insightIndicatorStatus &&
|
||||
{title === 'App Insights' && insightsIndicatorStatus &&
|
||||
<InsightsIndicatorDot
|
||||
size="small"
|
||||
top={4}
|
||||
|
||||
@ -8,7 +8,7 @@ import { testStorageConnection } from '@/platforms/storage';
|
||||
import { APP_CONFIGURATION } from '@/app/config';
|
||||
import { getStorageUploadUrlsNoStore } from '@/platforms/storage/cache';
|
||||
import { getPhotosMetaCached, getUniqueTagsCached } from '@/photo/cache';
|
||||
import { getShouldShowInsightsIndicator } from '@/admin/insights/server';
|
||||
import { getInsightsIndicatorStatus } from '@/admin/insights/server';
|
||||
|
||||
export const getAdminDataAction = async () =>
|
||||
runAuthenticatedAdminServerAction(async () => {
|
||||
@ -17,7 +17,7 @@ export const getAdminDataAction = async () =>
|
||||
countHiddenPhotos,
|
||||
countTags,
|
||||
countUploads,
|
||||
shouldShowInsightsIndicator,
|
||||
insightsIndicatorStatus,
|
||||
] = await Promise.all([
|
||||
getPhotosMetaCached()
|
||||
.then(({ count }) => count)
|
||||
@ -34,7 +34,7 @@ export const getAdminDataAction = async () =>
|
||||
console.error(`Error getting blob upload urls: ${e}`);
|
||||
return 0;
|
||||
}),
|
||||
getShouldShowInsightsIndicator(),
|
||||
getInsightsIndicatorStatus(),
|
||||
]);
|
||||
|
||||
return {
|
||||
@ -42,7 +42,7 @@ export const getAdminDataAction = async () =>
|
||||
countHiddenPhotos,
|
||||
countTags,
|
||||
countUploads,
|
||||
shouldShowInsightsIndicator,
|
||||
insightsIndicatorStatus,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@ -11,11 +11,13 @@ import {
|
||||
GRID_HOMEPAGE_ENABLED,
|
||||
HAS_STATIC_OPTIMIZATION,
|
||||
MATTE_PHOTOS,
|
||||
SHOW_SIDEBAR_CAMERAS_FIRST,
|
||||
} from '@/app/config';
|
||||
import { getGitHubMetaForCurrentApp, getSignificantInsights } from '.';
|
||||
import { getOutdatedPhotosCount } from '@/photo/db/query';
|
||||
|
||||
const BASIC_PHOTO_INSTALLATION_COUNT = 32;
|
||||
const TAG_COUNT_THRESHOLD = 12;
|
||||
|
||||
export default async function AdminAppInsights() {
|
||||
const [
|
||||
@ -63,6 +65,10 @@ export default async function AdminAppInsights() {
|
||||
noConfiguredDomain,
|
||||
outdatedPhotos,
|
||||
photoMatting: photosCountPortrait > 0 && !MATTE_PHOTOS,
|
||||
camerasFirst: (
|
||||
tags.length > TAG_COUNT_THRESHOLD &&
|
||||
!SHOW_SIDEBAR_CAMERAS_FIRST
|
||||
),
|
||||
gridFirst: (
|
||||
photosCount >= BASIC_PHOTO_INSTALLATION_COUNT &&
|
||||
!GRID_HOMEPAGE_ENABLED
|
||||
|
||||
@ -7,6 +7,7 @@ import PhotoFilmSimulationIcon from '@/simulation/PhotoFilmSimulationIcon';
|
||||
import { FaCamera } from 'react-icons/fa';
|
||||
import { FaTag } from 'react-icons/fa';
|
||||
import { FaCircleInfo, FaRegCalendar } from 'react-icons/fa6';
|
||||
import { HiMiniArrowsUpDown } from 'react-icons/hi2';
|
||||
import { HiOutlinePhotograph } from 'react-icons/hi';
|
||||
import { MdAspectRatio } from 'react-icons/md';
|
||||
import { PiWarningBold } from 'react-icons/pi';
|
||||
@ -107,6 +108,7 @@ export default function AdminAppInsightsClient({
|
||||
noConfiguredDomain,
|
||||
outdatedPhotos,
|
||||
photoMatting,
|
||||
camerasFirst,
|
||||
gridFirst,
|
||||
noStaticOptimization,
|
||||
} = insights;
|
||||
@ -271,7 +273,10 @@ export default function AdminAppInsightsClient({
|
||||
Not explicitly setting a domain may cause certain features
|
||||
to behave unexpectedly. Domains are stored in
|
||||
{' '}
|
||||
<EnvVar variable="NEXT_PUBLIC_SITE_DOMAIN" />.
|
||||
<EnvVar
|
||||
variable="NEXT_PUBLIC_SITE_DOMAIN"
|
||||
trailingContent="."
|
||||
/>
|
||||
</>}
|
||||
/>}
|
||||
{(noStaticOptimization || debug) && <ScoreCardRow
|
||||
@ -313,7 +318,10 @@ export default function AdminAppInsightsClient({
|
||||
expandContent={<>
|
||||
Enable automatic AI text generation
|
||||
{' '}
|
||||
by setting <EnvVar variable="OPENAI_SECRET_KEY" />.
|
||||
by setting <EnvVar
|
||||
variable="OPENAI_SECRET_KEY"
|
||||
trailingContent="."
|
||||
/>
|
||||
{' '}
|
||||
Further instruction and cost considerations in
|
||||
{' '}
|
||||
@ -331,7 +339,28 @@ export default function AdminAppInsightsClient({
|
||||
{' '}
|
||||
portrait and landscape photos appear more consistent
|
||||
{' '}
|
||||
<EnvVar variable="NEXT_PUBLIC_MATTE_PHOTOS" value="1" />.
|
||||
<EnvVar
|
||||
variable="NEXT_PUBLIC_MATTE_PHOTOS"
|
||||
value="1"
|
||||
trailingContent="."
|
||||
/>
|
||||
</>}
|
||||
/>}
|
||||
{(camerasFirst || debug) && <ScoreCardRow
|
||||
icon={<HiMiniArrowsUpDown
|
||||
size={17}
|
||||
className="translate-x-[-1px]"
|
||||
/>}
|
||||
content="Move cameras above tags in sidebar"
|
||||
expandContent={<>
|
||||
Now that you have more than a few tags, consider
|
||||
showing cameras first in the sidebar by setting
|
||||
{' '}
|
||||
<EnvVar
|
||||
variable="SHOW_SIDEBAR_CAMERAS_FIRST"
|
||||
value="1"
|
||||
trailingContent="."
|
||||
/>
|
||||
</>}
|
||||
/>}
|
||||
{(gridFirst || debug) && <ScoreCardRow
|
||||
|
||||
@ -19,7 +19,7 @@ export default function InsightsIndicatorDot({
|
||||
bottom?: number
|
||||
left?: number
|
||||
}) {
|
||||
const { insightIndicatorStatus } = useAppState();
|
||||
const { insightsIndicatorStatus } = useAppState();
|
||||
|
||||
const getSize = () => {
|
||||
switch (size) {
|
||||
@ -39,7 +39,7 @@ export default function InsightsIndicatorDot({
|
||||
bottom !== undefined ||
|
||||
left !== undefined
|
||||
) && 'absolute',
|
||||
(colorOverride ?? insightIndicatorStatus) === 'blue'
|
||||
(colorOverride ?? insightsIndicatorStatus) === 'blue'
|
||||
? 'text-blue-500'
|
||||
: 'text-amber-500',
|
||||
className,
|
||||
|
||||
@ -10,41 +10,39 @@ import {
|
||||
import { PhotoDateRange } from '@/photo';
|
||||
import { getGitHubMeta } from '@/platforms/github';
|
||||
|
||||
type AdminAppInsightCode =
|
||||
'noFork' |
|
||||
'forkBehind';
|
||||
const AdminAppInsightCode = [
|
||||
'noFork',
|
||||
'forkBehind',
|
||||
] as const;
|
||||
type AdminAppInsightCode = typeof AdminAppInsightCode[number];
|
||||
|
||||
type AdminAppInsightRecommendation =
|
||||
'noAi' |
|
||||
'noAiRateLimiting' |
|
||||
'noConfiguredDomain' |
|
||||
'photoMatting' |
|
||||
'gridFirst' |
|
||||
'noStaticOptimization';
|
||||
const _INSIGHTS_TEMPLATE = [
|
||||
'noAi',
|
||||
'noAiRateLimiting',
|
||||
'noConfiguredDomain',
|
||||
'photoMatting',
|
||||
'camerasFirst',
|
||||
'gridFirst',
|
||||
'noStaticOptimization',
|
||||
] as const;
|
||||
type AdminAppInsightRecommendation = typeof _INSIGHTS_TEMPLATE[number];
|
||||
|
||||
type AdminAppInsightLibrary =
|
||||
'outdatedPhotos';
|
||||
const _INSIGHTS_LIBRARY = [
|
||||
'outdatedPhotos',
|
||||
] as const;
|
||||
type AdminAppInsightLibrary = typeof _INSIGHTS_LIBRARY[number];
|
||||
|
||||
export type AdminAppInsight =
|
||||
AdminAppInsightCode |
|
||||
AdminAppInsightRecommendation |
|
||||
AdminAppInsightLibrary;
|
||||
|
||||
const RECOMMENDATIONS: AdminAppInsightRecommendation[] = [
|
||||
'noAi',
|
||||
'noAiRateLimiting',
|
||||
'noConfiguredDomain',
|
||||
'photoMatting',
|
||||
'gridFirst',
|
||||
'noStaticOptimization',
|
||||
];
|
||||
|
||||
export type AdminAppInsights = Record<AdminAppInsight, boolean>
|
||||
|
||||
export type InsightIndicatorStatus = 'blue' | 'yellow' | undefined;
|
||||
export type InsightsIndicatorStatus = 'blue' | 'yellow' | undefined;
|
||||
|
||||
export const hasTemplateRecommendations = (insights: AdminAppInsights) =>
|
||||
RECOMMENDATIONS.some(insight => insights[insight]);
|
||||
_INSIGHTS_TEMPLATE.some(insight => insights[insight]);
|
||||
|
||||
export interface PhotoStats {
|
||||
photosCount: number
|
||||
@ -87,3 +85,20 @@ export const getSignificantInsights = ({
|
||||
outdatedPhotos: Boolean(photosCountOutdated),
|
||||
};
|
||||
};
|
||||
|
||||
export const indicatorStatusForSignificantInsights = (
|
||||
insights: Awaited<ReturnType<typeof getSignificantInsights>>,
|
||||
) => {
|
||||
const {
|
||||
forkBehind,
|
||||
noAiRateLimiting,
|
||||
noConfiguredDomain,
|
||||
outdatedPhotos,
|
||||
} = insights;
|
||||
|
||||
if (noAiRateLimiting || noConfiguredDomain) {
|
||||
return 'yellow';
|
||||
} else if (forkBehind || outdatedPhotos) {
|
||||
return 'blue';
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
import { getOutdatedPhotosCount } from '@/photo/db/query';
|
||||
import { getSignificantInsights } from '.';
|
||||
import {
|
||||
getSignificantInsights,
|
||||
indicatorStatusForSignificantInsights,
|
||||
} from '.';
|
||||
import { getGitHubMetaForCurrentApp } from '.';
|
||||
|
||||
export const getShouldShowInsightsIndicator = async () => {
|
||||
export const getInsightsIndicatorStatus = async () => {
|
||||
const [
|
||||
codeMeta,
|
||||
photosCountOutdated,
|
||||
@ -11,19 +14,10 @@ export const getShouldShowInsightsIndicator = async () => {
|
||||
getOutdatedPhotosCount(),
|
||||
]);
|
||||
|
||||
const {
|
||||
forkBehind,
|
||||
noAiRateLimiting,
|
||||
noConfiguredDomain,
|
||||
outdatedPhotos,
|
||||
} = getSignificantInsights({
|
||||
const significantInsights = getSignificantInsights({
|
||||
codeMeta,
|
||||
photosCountOutdated,
|
||||
});
|
||||
|
||||
if (noAiRateLimiting || noConfiguredDomain) {
|
||||
return 'yellow';
|
||||
} else if (forkBehind || outdatedPhotos) {
|
||||
return 'blue';
|
||||
}
|
||||
return indicatorStatusForSignificantInsights(significantInsights);
|
||||
};
|
||||
|
||||
@ -221,6 +221,8 @@ export const SHOW_RECIPES =
|
||||
process.env.NEXT_PUBLIC_HIDE_RECIPES !== '1';
|
||||
export const SHOW_REPO_LINK =
|
||||
process.env.NEXT_PUBLIC_HIDE_REPO_LINK !== '1';
|
||||
export const SHOW_SIDEBAR_CAMERAS_FIRST =
|
||||
process.env.NEXT_PUBLIC_CAMERAS_FIRST === '1';
|
||||
|
||||
// GRID
|
||||
|
||||
@ -317,6 +319,7 @@ export const APP_CONFIGURATION = {
|
||||
showFilmSimulations: SHOW_FILM_SIMULATIONS,
|
||||
showRecipes: SHOW_RECIPES,
|
||||
showRepoLink: SHOW_REPO_LINK,
|
||||
showSidebarCamerasFirst: SHOW_SIDEBAR_CAMERAS_FIRST,
|
||||
// Grid
|
||||
isGridHomepageEnabled: GRID_HOMEPAGE_ENABLED,
|
||||
gridAspectRatio: GRID_ASPECT_RATIO,
|
||||
|
||||
@ -34,13 +34,18 @@ export default function EnvVar({
|
||||
{variable}{value && ` = ${value}`}
|
||||
</span>
|
||||
{includeCopyButton &&
|
||||
<CopyButton
|
||||
className="translate-y-[0.5px]"
|
||||
label={variable}
|
||||
text={variable}
|
||||
subtle
|
||||
/>}
|
||||
{trailingContent}
|
||||
<span className="translate-y-[1px]">
|
||||
<CopyButton
|
||||
className=""
|
||||
label={variable}
|
||||
text={variable}
|
||||
subtle
|
||||
/>
|
||||
</span>}
|
||||
{trailingContent &&
|
||||
<span className="-ml-0.5">
|
||||
{trailingContent}
|
||||
</span>}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -108,7 +108,7 @@ export default function CommandKClient({
|
||||
tagsCount,
|
||||
selectedPhotoIds,
|
||||
setSelectedPhotoIds,
|
||||
insightIndicatorStatus,
|
||||
insightsIndicatorStatus,
|
||||
isGridHighDensity,
|
||||
areZoomControlsShown,
|
||||
arePhotosMatted,
|
||||
@ -365,7 +365,7 @@ export default function CommandKClient({
|
||||
adminSection.items.push({
|
||||
label: <span className="flex items-center gap-3">
|
||||
App Insights
|
||||
{insightIndicatorStatus &&
|
||||
{insightsIndicatorStatus &&
|
||||
<InsightsIndicatorDot />}
|
||||
</span>,
|
||||
keywords: ['app insights'],
|
||||
|
||||
@ -15,7 +15,7 @@ import FavsTag from '../tag/FavsTag';
|
||||
import { useAppState } from '@/state/AppState';
|
||||
import { useMemo } from 'react';
|
||||
import HiddenTag from '@/tag/HiddenTag';
|
||||
import { SITE_ABOUT } from '@/app/config';
|
||||
import { SHOW_SIDEBAR_CAMERAS_FIRST, SITE_ABOUT } from '@/app/config';
|
||||
import {
|
||||
htmlHasBrParagraphBreaks,
|
||||
safelyParseFormattedHtml,
|
||||
@ -43,6 +43,107 @@ export default function PhotoGridSidebar({
|
||||
addHiddenToTags(tags, photosCountHidden)
|
||||
, [tags, photosCountHidden]);
|
||||
|
||||
const tagsContent = tags.length > 0
|
||||
? <HeaderList
|
||||
title='Tags'
|
||||
icon={<FaTag
|
||||
size={12}
|
||||
className="text-icon translate-y-[1px]"
|
||||
/>}
|
||||
items={tagsIncludingHidden.map(({ tag, count }) => {
|
||||
switch (tag) {
|
||||
case TAG_FAVS:
|
||||
return <FavsTag
|
||||
key={TAG_FAVS}
|
||||
countOnHover={count}
|
||||
type="icon-last"
|
||||
prefetch={false}
|
||||
contrast="low"
|
||||
badged
|
||||
/>;
|
||||
case TAG_HIDDEN:
|
||||
return <HiddenTag
|
||||
key={TAG_HIDDEN}
|
||||
countOnHover={count}
|
||||
type="icon-last"
|
||||
prefetch={false}
|
||||
contrast="low"
|
||||
badged
|
||||
/>;
|
||||
default:
|
||||
return <PhotoTag
|
||||
key={tag}
|
||||
tag={tag}
|
||||
type="text-only"
|
||||
countOnHover={count}
|
||||
prefetch={false}
|
||||
contrast="low"
|
||||
badged
|
||||
/>;
|
||||
}
|
||||
})}
|
||||
/>
|
||||
: null;
|
||||
|
||||
const camerasContent = cameras.length > 0
|
||||
? <HeaderList
|
||||
title="Cameras"
|
||||
icon={<IoMdCamera
|
||||
size={13}
|
||||
className="text-icon translate-y-[-0.25px]"
|
||||
/>}
|
||||
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 filmsContent = simulations.length > 0
|
||||
? <HeaderList
|
||||
title="Films"
|
||||
icon={<PhotoFilmSimulationIcon
|
||||
className="translate-y-[0.5px]"
|
||||
/>}
|
||||
items={simulations
|
||||
.sort(sortFilmSimulationsWithCount)
|
||||
.map(({ simulation, count }) =>
|
||||
<div
|
||||
key={simulation}
|
||||
className="translate-x-[-2px]"
|
||||
>
|
||||
<PhotoFilmSimulation
|
||||
simulation={simulation}
|
||||
countOnHover={count}
|
||||
type="text-only"
|
||||
prefetch={false}
|
||||
/>
|
||||
</div>)}
|
||||
/>
|
||||
: null;
|
||||
|
||||
const photoStatsContent = photosCount > 0
|
||||
? start
|
||||
? <HeaderList
|
||||
title={photoQuantityText(photosCount, false)}
|
||||
items={start === end
|
||||
? [start]
|
||||
: [`${end} –`, start]}
|
||||
/>
|
||||
: <HeaderList
|
||||
items={[photoQuantityText(photosCount, false)]}
|
||||
/>
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{SITE_ABOUT && <HeaderList
|
||||
@ -57,95 +158,11 @@ export default function PhotoGridSidebar({
|
||||
}}
|
||||
/>]}
|
||||
/>}
|
||||
{tags.length > 0 && <HeaderList
|
||||
title='Tags'
|
||||
icon={<FaTag
|
||||
size={12}
|
||||
className="text-icon translate-y-[1px]"
|
||||
/>}
|
||||
items={tagsIncludingHidden.map(({ tag, count }) => {
|
||||
switch (tag) {
|
||||
case TAG_FAVS:
|
||||
return <FavsTag
|
||||
key={TAG_FAVS}
|
||||
countOnHover={count}
|
||||
type="icon-last"
|
||||
prefetch={false}
|
||||
contrast="low"
|
||||
badged
|
||||
/>;
|
||||
case TAG_HIDDEN:
|
||||
return <HiddenTag
|
||||
key={TAG_HIDDEN}
|
||||
countOnHover={count}
|
||||
type="icon-last"
|
||||
prefetch={false}
|
||||
contrast="low"
|
||||
badged
|
||||
/>;
|
||||
default:
|
||||
return <PhotoTag
|
||||
key={tag}
|
||||
tag={tag}
|
||||
type="text-only"
|
||||
countOnHover={count}
|
||||
prefetch={false}
|
||||
contrast="low"
|
||||
badged
|
||||
/>;
|
||||
}
|
||||
})}
|
||||
/>}
|
||||
{cameras.length > 0 && <HeaderList
|
||||
title="Cameras"
|
||||
icon={<IoMdCamera
|
||||
size={13}
|
||||
className="text-icon translate-y-[-0.25px]"
|
||||
/>}
|
||||
items={cameras
|
||||
.sort(sortCamerasWithCount)
|
||||
.map(({ cameraKey, camera, count }) =>
|
||||
<PhotoCamera
|
||||
key={cameraKey}
|
||||
camera={camera}
|
||||
type="text-only"
|
||||
countOnHover={count}
|
||||
prefetch={false}
|
||||
contrast="low"
|
||||
hideAppleIcon
|
||||
badged
|
||||
/>)}
|
||||
/>}
|
||||
{simulations.length > 0 && <HeaderList
|
||||
title="Films"
|
||||
icon={<PhotoFilmSimulationIcon
|
||||
className="translate-y-[0.5px]"
|
||||
/>}
|
||||
items={simulations
|
||||
.sort(sortFilmSimulationsWithCount)
|
||||
.map(({ simulation, count }) =>
|
||||
<div
|
||||
key={simulation}
|
||||
className="translate-x-[-2px]"
|
||||
>
|
||||
<PhotoFilmSimulation
|
||||
simulation={simulation}
|
||||
countOnHover={count}
|
||||
type="text-only"
|
||||
prefetch={false}
|
||||
/>
|
||||
</div>)}
|
||||
/>}
|
||||
{photosCount > 0 && start
|
||||
? <HeaderList
|
||||
title={photoQuantityText(photosCount, false)}
|
||||
items={start === end
|
||||
? [start]
|
||||
: [`${end} –`, start]}
|
||||
/>
|
||||
: <HeaderList
|
||||
items={[photoQuantityText(photosCount, false)]}
|
||||
/>}
|
||||
{SHOW_SIDEBAR_CAMERAS_FIRST
|
||||
? <>{camerasContent}{tagsContent}</>
|
||||
: <>{tagsContent}{camerasContent}</>}
|
||||
{filmsContent}
|
||||
{photoStatsContent}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
} from 'react';
|
||||
import { AnimationConfig } from '@/components/AnimateItems';
|
||||
import { ShareModalProps } from '@/share';
|
||||
import { InsightIndicatorStatus } from '@/admin/insights';
|
||||
import { InsightsIndicatorStatus } from '@/admin/insights';
|
||||
import { INITIAL_UPLOAD_STATE, UploadState } from '@/admin/upload';
|
||||
|
||||
export interface AppStateContext {
|
||||
@ -52,8 +52,8 @@ export interface AppStateContext {
|
||||
setSelectedPhotoIds?: Dispatch<SetStateAction<string[] | undefined>>
|
||||
isPerformingSelectEdit?: boolean
|
||||
setIsPerformingSelectEdit?: Dispatch<SetStateAction<boolean>>
|
||||
insightIndicatorStatus?: InsightIndicatorStatus
|
||||
setInsightIndicatorStatus?: Dispatch<SetStateAction<InsightIndicatorStatus>>
|
||||
insightsIndicatorStatus?: InsightsIndicatorStatus
|
||||
setInsightsIndicatorStatus?: Dispatch<SetStateAction<InsightsIndicatorStatus>>
|
||||
// DEBUG
|
||||
isGridHighDensity?: boolean
|
||||
setIsGridHighDensity?: Dispatch<SetStateAction<boolean>>
|
||||
|
||||
@ -14,7 +14,7 @@ import {
|
||||
} from '@/app/config';
|
||||
import { ShareModalProps } from '@/share';
|
||||
import { storeTimezoneCookie } from '@/utility/timezone';
|
||||
import { InsightIndicatorStatus } from '@/admin/insights';
|
||||
import { InsightsIndicatorStatus } from '@/admin/insights';
|
||||
import { getAdminDataAction } from '@/admin/actions';
|
||||
import {
|
||||
storeAuthEmailCookie,
|
||||
@ -72,8 +72,8 @@ export default function AppStateProvider({
|
||||
useState<string[] | undefined>();
|
||||
const [isPerformingSelectEdit, setIsPerformingSelectEdit] =
|
||||
useState(false);
|
||||
const [insightIndicatorStatus, setInsightIndicatorStatus] =
|
||||
useState<InsightIndicatorStatus>();
|
||||
const [insightsIndicatorStatus, setInsightsIndicatorStatus] =
|
||||
useState<InsightsIndicatorStatus>();
|
||||
// DEBUG
|
||||
const [isGridHighDensity, setIsGridHighDensity] =
|
||||
useState(HIGH_DENSITY_GRID);
|
||||
@ -138,7 +138,7 @@ export default function AppStateProvider({
|
||||
setPhotosCountHidden(adminData.countHiddenPhotos);
|
||||
setUploadsCount(adminData.countUploads);
|
||||
setTagsCount(adminData.countTags);
|
||||
setInsightIndicatorStatus(adminData.shouldShowInsightsIndicator);
|
||||
setInsightsIndicatorStatus(adminData.insightsIndicatorStatus);
|
||||
}
|
||||
} else {
|
||||
setPhotosCountHidden(0);
|
||||
@ -205,8 +205,8 @@ export default function AppStateProvider({
|
||||
setSelectedPhotoIds,
|
||||
isPerformingSelectEdit,
|
||||
setIsPerformingSelectEdit,
|
||||
insightIndicatorStatus,
|
||||
setInsightIndicatorStatus,
|
||||
insightsIndicatorStatus,
|
||||
setInsightsIndicatorStatus,
|
||||
// DEBUG
|
||||
isGridHighDensity,
|
||||
setIsGridHighDensity,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user