Add visual indicator for important insights

This commit is contained in:
Sam Becker 2025-02-16 23:31:12 -06:00
parent 7c98c55853
commit c64e5b13df
7 changed files with 109 additions and 53 deletions

View File

@ -10,25 +10,14 @@ import {
APP_CONFIGURATION,
GRID_HOMEPAGE_ENABLED,
HAS_STATIC_OPTIMIZATION,
IS_DEVELOPMENT,
IS_PRODUCTION,
IS_VERCEL_GIT_PROVIDER_GITHUB,
MATTE_PHOTOS,
VERCEL_GIT_BRANCH,
VERCEL_GIT_COMMIT_SHA,
VERCEL_GIT_REPO_OWNER,
VERCEL_GIT_REPO_SLUG,
} from '@/app-core/config';
import { getGitHubMeta } from '../../platforms/github';
import { OUTDATED_THRESHOLD } from '@/photo';
import { getGitHubMetaForCurrentApp, getSignificantInsights } from '.';
const BASIC_PHOTO_INSTALLATION_COUNT = 32;
const owner = VERCEL_GIT_REPO_OWNER;
const repo = VERCEL_GIT_REPO_SLUG;
const branch = VERCEL_GIT_BRANCH;
const commit = VERCEL_GIT_COMMIT_SHA;
export default async function AdminAppInsights() {
const [
{ count: photosCount, dateRange },
@ -49,30 +38,30 @@ export default async function AdminAppInsights() {
getUniqueCameras(),
getUniqueFilmSimulations(),
getUniqueFocalLengths(),
IS_VERCEL_GIT_PROVIDER_GITHUB || IS_DEVELOPMENT
? getGitHubMeta({
owner,
repo,
branch,
commit,
})
: undefined,
getGitHubMetaForCurrentApp(),
]);
const { isAiTextGenerationEnabled } = APP_CONFIGURATION;
const {
isAiTextGenerationEnabled,
hasVercelBlobStorage,
} = APP_CONFIGURATION;
noFork,
forkBehind,
noAiRateLimiting,
outdatedPhotos,
} = getSignificantInsights({
codeMeta,
photosCountOutdated,
});
return (
<AdminAppInsightsClient
codeMeta={codeMeta}
insights={{
noFork: !codeMeta?.isForkedFromBase && !codeMeta?.isBaseRepo,
forkBehind: Boolean(codeMeta?.isBehind),
noFork,
forkBehind,
noAi: !isAiTextGenerationEnabled,
noAiRateLimiting: isAiTextGenerationEnabled && !hasVercelBlobStorage,
outdatedPhotos: Boolean(photosCountOutdated),
noAiRateLimiting,
outdatedPhotos,
photoMatting: photosCountPortrait > 0 && !MATTE_PHOTOS,
gridFirst: (
photosCount >= BASIC_PHOTO_INSTALLATION_COUNT &&

View File

@ -11,7 +11,6 @@ import { HiOutlinePhotograph } from 'react-icons/hi';
import { MdAspectRatio } from 'react-icons/md';
import { PiWarningBold } from 'react-icons/pi';
import { TbCone, TbSparkles } from 'react-icons/tb';
import { getGitHubMeta } from '../../platforms/github';
import { BiGitBranch, BiGitCommit, BiLogoGithub } from 'react-icons/bi';
import {
TEMPLATE_REPO_BRANCH,
@ -22,7 +21,12 @@ import {
TEMPLATE_REPO_URL_FORK,
TEMPLATE_REPO_URL_README,
} from '@/app-core/config';
import { AdminAppInsights, hasTemplateRecommendations, PhotoStats } from '.';
import {
AdminAppInsights,
getGitHubMetaForCurrentApp,
hasTemplateRecommendations,
PhotoStats,
} from '.';
import EnvVar from '@/components/EnvVar';
import { IoSyncCircle } from 'react-icons/io5';
import clsx from 'clsx/lite';
@ -82,7 +86,7 @@ export default function AdminAppInsightsClient({
},
debug,
}: {
codeMeta?: Awaited<ReturnType<typeof getGitHubMeta>>
codeMeta?: Awaited<ReturnType<typeof getGitHubMetaForCurrentApp>>
insights: AdminAppInsights
photoStats: PhotoStats
debug?: boolean

View File

@ -1,26 +1,23 @@
import { useAppState } from '@/state/AppState';
import clsx from 'clsx/lite';
import { LuLightbulb } from 'react-icons/lu';
export default function AdminAppInsightsIcon({
indicator,
}: {
indicator?: 'blue' | 'yellow'
}) {
export default function AdminAppInsightsIcon() {
const {
shouldShowInsightsIndicator,
} = useAppState();
return (
<span className="inline-flex relative">
<LuLightbulb
size={19}
className="translate-y-[3px]"
/>
{indicator && <span className={clsx(
{shouldShowInsightsIndicator && <span className={clsx(
'absolute',
'top-[2px] right-[0.5px]',
'size-2 rounded-full',
indicator === 'yellow'
? 'bg-amber-500'
: indicator === 'blue'
? 'bg-blue-500'
: undefined,
'bg-blue-500',
)} />}
</span>
);

View File

@ -0,0 +1,26 @@
'use server';
import { runAuthenticatedAdminServerAction } from '@/auth';
import { getGitHubMetaForCurrentApp, getSignificantInsights } from '.';
import { getPhotosMeta } from '@/photo/db/query';
import { OUTDATED_THRESHOLD } from '@/photo';
export const getShouldShowInsightsIndicatorAction = async () =>
runAuthenticatedAdminServerAction(async () => {
const [
codeMeta,
{ count: photosCountOutdated },
] = await Promise.all([
getGitHubMetaForCurrentApp(),
getPhotosMeta({ hidden: 'include', updatedBefore: OUTDATED_THRESHOLD }),
]);
const significantInsights = getSignificantInsights({
codeMeta,
photosCountOutdated,
});
return Object
.values(significantInsights)
.some(Boolean);
});

View File

@ -1,4 +1,14 @@
import {
VERCEL_GIT_BRANCH,
VERCEL_GIT_COMMIT_SHA,
VERCEL_GIT_REPO_OWNER,
VERCEL_GIT_REPO_SLUG,
IS_VERCEL_GIT_PROVIDER_GITHUB,
IS_DEVELOPMENT,
APP_CONFIGURATION,
} from '@/app-core/config';
import { PhotoDateRange } from '@/photo';
import { getGitHubMeta } from '@/platforms/github';
export type AdminAppInsight =
'noFork' |
@ -34,11 +44,32 @@ export interface PhotoStats {
dateRange?: PhotoDateRange
}
export const getInsightIndicator = ({
forkBehind,
noAiRateLimiting,
outdatedPhotos,
}: AdminAppInsights) =>
forkBehind ||
noAiRateLimiting ||
outdatedPhotos;
export const getGitHubMetaForCurrentApp = () =>
(IS_VERCEL_GIT_PROVIDER_GITHUB || IS_DEVELOPMENT)
? getGitHubMeta({
owner: VERCEL_GIT_REPO_OWNER,
repo: VERCEL_GIT_REPO_SLUG,
branch: VERCEL_GIT_BRANCH,
commit: VERCEL_GIT_COMMIT_SHA,
})
: undefined;
export const getSignificantInsights = ({
codeMeta,
photosCountOutdated,
}: {
codeMeta: Awaited<ReturnType<typeof getGitHubMetaForCurrentApp>>
photosCountOutdated: number
}) => {
const {
isAiTextGenerationEnabled,
hasRedisStorage,
} = APP_CONFIGURATION;
return {
noFork: !codeMeta?.isForkedFromBase && !codeMeta?.isBaseRepo,
forkBehind: Boolean(codeMeta?.isBehind),
noAiRateLimiting: isAiTextGenerationEnabled && !hasRedisStorage,
outdatedPhotos: Boolean(photosCountOutdated),
};
};

View File

@ -30,6 +30,8 @@ export interface AppStateContext {
setSelectedPhotoIds?: Dispatch<SetStateAction<string[] | undefined>>
isPerformingSelectEdit?: boolean
setIsPerformingSelectEdit?: Dispatch<SetStateAction<boolean>>
shouldShowInsightsIndicator?: boolean
setShouldShowInsightsIndicator?: Dispatch<SetStateAction<boolean>>
// DEBUG
isGridHighDensity?: boolean
setIsGridHighDensity?: Dispatch<SetStateAction<boolean>>

View File

@ -14,6 +14,7 @@ import {
import { getPhotosHiddenMetaCachedAction } from '@/photo/actions';
import { ShareModalProps } from '@/share';
import { storeTimezoneCookie } from '@/utility/timezone';
import { getShouldShowInsightsIndicatorAction } from '@/admin/insights/actions';
export default function AppStateProvider({
children,
@ -47,6 +48,8 @@ export default function AppStateProvider({
useState<string[] | undefined>();
const [isPerformingSelectEdit, setIsPerformingSelectEdit] =
useState(false);
const [shouldShowInsightsIndicator, setShouldShowInsightsIndicator] =
useState(false);
// DEBUG
const [isGridHighDensity, setIsGridHighDensity] =
useState(HIGH_DENSITY_GRID);
@ -70,10 +73,12 @@ export default function AppStateProvider({
const isUserSignedIn = Boolean(userEmail);
useEffect(() => {
if (isUserSignedIn) {
const timeout = setTimeout(() =>
getPhotosHiddenMetaCachedAction().then(({ count }) =>
setHiddenPhotosCount(count))
, 100);
const timeout = setTimeout(() =>{
getPhotosHiddenMetaCachedAction()
.then(({ count }) => setHiddenPhotosCount(count));
getShouldShowInsightsIndicatorAction()
.then(setShouldShowInsightsIndicator);
}, 100);
return () => clearTimeout(timeout);
} else {
setHiddenPhotosCount(0);
@ -119,6 +124,8 @@ export default function AppStateProvider({
setSelectedPhotoIds,
isPerformingSelectEdit,
setIsPerformingSelectEdit,
shouldShowInsightsIndicator,
setShouldShowInsightsIndicator,
// DEBUG
isGridHighDensity,
setIsGridHighDensity,