Add configuration for nav site title

This commit is contained in:
Sam Becker 2025-03-25 14:14:00 -05:00
parent cf39b49536
commit 70a2cb3c37
9 changed files with 60 additions and 43 deletions

View File

@ -105,9 +105,10 @@ _⚠ READ BEFORE PROCEEDING_
Application behavior can be changed by configuring the following environment variables: Application behavior can be changed by configuring the following environment variables:
#### Content #### Content
- `NEXT_PUBLIC_SITE_TITLE` (seen in browser tab) - `NEXT_PUBLIC_META_TITLE` (seen in browser tab)
- `NEXT_PUBLIC_SITE_DESCRIPTION` (seen in nav, beneath title) - `NEXT_PUBLIC_META_DESCRIPTION` (seen in nav, beneath title)
- `NEXT_PUBLIC_SITE_ABOUT` (seen in grid sidebar—accepts rich formatting tags: `<b>`, `<strong>`, `<i>`, `<em>`, `<u>`, `<br>`) - `NEXT_PUBLIC_NAV_TITLE` (seen in navigation, defaults to domain when not configured)
- `NEXT_PUBLIC_PAGE_ABOUT` (seen in grid sidebar—accepts rich formatting tags: `<b>`, `<strong>`, `<i>`, `<em>`, `<u>`, `<br>`)
#### Performance #### Performance
> ⚠️ Enabling may result in increased project usage. Static optimization [troubleshooting hints](#why-do-production-deployments-fail-when-static-optimization-is-enabled) in FAQ. > ⚠️ Enabling may result in increased project usage. Static optimization [troubleshooting hints](#why-do-production-deployments-fail-when-static-optimization-is-enabled) in FAQ.

View File

@ -3,7 +3,7 @@ import { API_PHOTO_REQUEST_LIMIT, formatPhotoForApi } from '@/app/api';
import { import {
BASE_URL, BASE_URL,
PUBLIC_API_ENABLED, PUBLIC_API_ENABLED,
SITE_TITLE, META_TITLE,
} from '@/app/config'; } from '@/app/config';
export const dynamic = 'force-dynamic'; export const dynamic = 'force-dynamic';
@ -13,7 +13,7 @@ export async function GET() {
const photos = await getPhotosCached({ limit: API_PHOTO_REQUEST_LIMIT }); const photos = await getPhotosCached({ limit: API_PHOTO_REQUEST_LIMIT });
return Response.json({ return Response.json({
meta: { meta: {
title: SITE_TITLE, title: META_TITLE,
url: BASE_URL, url: BASE_URL,
}, },
photos: photos.map(formatPhotoForApi), photos: photos.map(formatPhotoForApi),

View File

@ -5,9 +5,9 @@ import {
BASE_URL, BASE_URL,
DEFAULT_THEME, DEFAULT_THEME,
PRESERVE_ORIGINAL_UPLOADS, PRESERVE_ORIGINAL_UPLOADS,
SITE_DESCRIPTION, META_DESCRIPTION,
SITE_DOMAIN_OR_TITLE, NAV_TITLE_OR_DOMAIN,
SITE_TITLE, META_TITLE,
} from '@/app/config'; } from '@/app/config';
import AppStateProvider from '@/state/AppStateProvider'; import AppStateProvider from '@/state/AppStateProvider';
import ToasterWithThemes from '@/toast/ToasterWithThemes'; import ToasterWithThemes from '@/toast/ToasterWithThemes';
@ -27,16 +27,16 @@ import RecipeModal from '@/recipe/RecipeModal';
import '../tailwind.css'; import '../tailwind.css';
export const metadata: Metadata = { export const metadata: Metadata = {
title: SITE_TITLE, title: META_TITLE,
description: SITE_DESCRIPTION, description: META_DESCRIPTION,
...BASE_URL && { metadataBase: new URL(BASE_URL) }, ...BASE_URL && { metadataBase: new URL(BASE_URL) },
openGraph: { openGraph: {
title: SITE_TITLE, title: META_TITLE,
description: SITE_DESCRIPTION, description: META_DESCRIPTION,
}, },
twitter: { twitter: {
title: SITE_TITLE, title: META_TITLE,
description: SITE_DESCRIPTION, description: META_DESCRIPTION,
}, },
icons: [{ icons: [{
url: '/favicon.ico', url: '/favicon.ico',
@ -85,7 +85,7 @@ export default function RootLayout({
'mx-3 mb-3', 'mx-3 mb-3',
'lg:mx-6 lg:mb-6', 'lg:mx-6 lg:mb-6',
)}> )}>
<Nav siteDomainOrTitle={SITE_DOMAIN_OR_TITLE} /> <Nav navTitleOrDomain={NAV_TITLE_OR_DOMAIN} />
<main> <main>
<ShareModals /> <ShareModals />
<RecipeModal /> <RecipeModal />

View File

@ -26,7 +26,7 @@ export function GET() {
url.searchParams.set('env-description', 'Configure your photo blog meta'); url.searchParams.set('env-description', 'Configure your photo blog meta');
url.searchParams.set('env-link', 'BLANK'); url.searchParams.set('env-link', 'BLANK');
url.searchParams.set('env', [ url.searchParams.set('env', [
'NEXT_PUBLIC_SITE_TITLE', 'NEXT_PUBLIC_META_TITLE',
].join(',')); ].join(','));
} }
url.searchParams.set('teamCreateStatus', 'hidden'); url.searchParams.set('teamCreateStatus', 'hidden');

View File

@ -318,7 +318,7 @@ export default function AdminAppConfigurationClient({
optional optional
> >
Store in environment variable (seen in browser tab): Store in environment variable (seen in browser tab):
{renderEnvVars(['NEXT_PUBLIC_SITE_TITLE'])} {renderEnvVars(['NEXT_PUBLIC_META_TITLE'])}
</ChecklistRow> </ChecklistRow>
<ChecklistRow <ChecklistRow
title="Add description" title="Add description"
@ -326,7 +326,7 @@ export default function AdminAppConfigurationClient({
optional optional
> >
Store in environment variable (seen in nav, under title): Store in environment variable (seen in nav, under title):
{renderEnvVars(['NEXT_PUBLIC_SITE_DESCRIPTION'])} {renderEnvVars(['NEXT_PUBLIC_META_DESCRIPTION'])}
</ChecklistRow> </ChecklistRow>
<ChecklistRow <ChecklistRow
title="Add about" title="Add about"
@ -334,7 +334,7 @@ export default function AdminAppConfigurationClient({
optional optional
> >
Store in environment variable (seen in grid sidebar): Store in environment variable (seen in grid sidebar):
{renderEnvVars(['NEXT_PUBLIC_SITE_ABOUT'])} {renderEnvVars(['NEXT_PUBLIC_PAGE_ABOUT'])}
</ChecklistRow> </ChecklistRow>
</ChecklistGroup> </ChecklistGroup>
{!simplifiedView && <> {!simplifiedView && <>

View File

@ -18,7 +18,7 @@ import AnimateItems from '../components/AnimateItems';
import { import {
GRID_HOMEPAGE_ENABLED, GRID_HOMEPAGE_ENABLED,
HAS_DEFINED_SITE_DESCRIPTION, HAS_DEFINED_SITE_DESCRIPTION,
SITE_DESCRIPTION, META_DESCRIPTION,
} from './config'; } from './config';
import { useRef } from 'react'; import { useRef } from 'react';
import useStickyNav from './useStickyNav'; import useStickyNav from './useStickyNav';
@ -28,9 +28,9 @@ const NAV_HEIGHT_CLASS = HAS_DEFINED_SITE_DESCRIPTION
: 'min-h-[4rem]'; : 'min-h-[4rem]';
export default function Nav({ export default function Nav({
siteDomainOrTitle, navTitleOrDomain,
}: { }: {
siteDomainOrTitle: string; navTitleOrDomain: string;
}) { }) {
const ref = useRef<HTMLElement>(null); const ref = useRef<HTMLElement>(null);
@ -102,14 +102,14 @@ export default function Nav({
'truncate overflow-hidden select-none', 'truncate overflow-hidden select-none',
HAS_DEFINED_SITE_DESCRIPTION && 'sm:font-bold', HAS_DEFINED_SITE_DESCRIPTION && 'sm:font-bold',
)}> )}>
{renderLink(siteDomainOrTitle, PATH_ROOT)} {renderLink(navTitleOrDomain, PATH_ROOT)}
</div> </div>
{HAS_DEFINED_SITE_DESCRIPTION && {HAS_DEFINED_SITE_DESCRIPTION &&
<div className={clsx( <div className={clsx(
'hidden sm:block truncate overflow-hidden', 'hidden sm:block truncate overflow-hidden',
'leading-tight', 'leading-tight',
)}> )}>
{SITE_DESCRIPTION} {META_DESCRIPTION}
</div>} </div>}
</div> </div>
</nav>] </nav>]

View File

@ -67,11 +67,7 @@ export const IS_BUILDING = process.env.NEXT_PHASE === 'phase-production-build';
export const VERCEL_BYPASS_KEY = 'x-vercel-protection-bypass'; export const VERCEL_BYPASS_KEY = 'x-vercel-protection-bypass';
export const VERCEL_BYPASS_SECRET = process.env.VERCEL_AUTOMATION_BYPASS_SECRET; export const VERCEL_BYPASS_SECRET = process.env.VERCEL_AUTOMATION_BYPASS_SECRET;
// SITE META // DOMAIN
export const SITE_TITLE =
process.env.NEXT_PUBLIC_SITE_TITLE ||
TEMPLATE_TITLE;
// User-facing domain, potential site title // User-facing domain, potential site title
const SITE_DOMAIN = const SITE_DOMAIN =
@ -91,17 +87,37 @@ export const BASE_URL = makeUrlAbsolute((
const SITE_DOMAIN_SHORT = shortenUrl(SITE_DOMAIN); const SITE_DOMAIN_SHORT = shortenUrl(SITE_DOMAIN);
export const SITE_DOMAIN_OR_TITLE = // SITE META
SITE_DOMAIN_SHORT ||
SITE_TITLE;
export const SITE_DESCRIPTION = export const NAV_TITLE =
process.env.NEXT_PUBLIC_NAV_TITLE;
export const META_TITLE =
process.env.NEXT_PUBLIC_META_TITLE ||
// Legacy environment variable
process.env.NEXT_PUBLIC_SITE_TITLE ||
NAV_TITLE ||
TEMPLATE_TITLE;
export const META_DESCRIPTION =
process.env.NEXT_PUBLIC_META_DESCRIPTION ||
// Legacy environment variable
process.env.NEXT_PUBLIC_SITE_DESCRIPTION || process.env.NEXT_PUBLIC_SITE_DESCRIPTION ||
SITE_DOMAIN; SITE_DOMAIN;
export const SITE_ABOUT = process.env.NEXT_PUBLIC_SITE_ABOUT; export const NAV_TITLE_OR_DOMAIN =
NAV_TITLE ||
SITE_DOMAIN_SHORT ||
META_TITLE;
export const PAGE_ABOUT =
process.env.NEXT_PUBLIC_PAGE_ABOUT ||
// Legacy environment variable
process.env.NEXT_PUBLIC_SITE_ABOUT;
export const HAS_DEFINED_SITE_DESCRIPTION = export const HAS_DEFINED_SITE_DESCRIPTION =
Boolean(process.env.NEXT_PUBLIC_META_DESCRIPTION) ||
// Legacy environment variable
Boolean(process.env.NEXT_PUBLIC_SITE_DESCRIPTION); Boolean(process.env.NEXT_PUBLIC_SITE_DESCRIPTION);
// STORAGE // STORAGE
@ -169,11 +185,11 @@ export const AI_TEXT_AUTO_GENERATED_FIELDS = parseAiAutoGeneratedFieldsString(
export const STATICALLY_OPTIMIZED_PHOTOS = export const STATICALLY_OPTIMIZED_PHOTOS =
process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS === '1' || process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS === '1' ||
// Legacy environment variable name // Legacy environment variable
process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_PAGES === '1'; process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_PAGES === '1';
export const STATICALLY_OPTIMIZED_PHOTO_OG_IMAGES = export const STATICALLY_OPTIMIZED_PHOTO_OG_IMAGES =
process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES === '1' || process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES === '1' ||
// Legacy environment variable name // Legacy environment variable
process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_OG_IMAGES === '1'; process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_OG_IMAGES === '1';
export const STATICALLY_OPTIMIZED_PHOTO_CATEGORIES = export const STATICALLY_OPTIMIZED_PHOTO_CATEGORIES =
process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES === '1'; process.env.NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES === '1';
@ -187,7 +203,7 @@ export const HAS_STATIC_OPTIMIZATION =
export const PRESERVE_ORIGINAL_UPLOADS = export const PRESERVE_ORIGINAL_UPLOADS =
process.env.NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS === '1' || process.env.NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS === '1' ||
// Legacy environment variable name // Legacy environment variable
process.env.NEXT_PUBLIC_PRO_MODE === '1'; process.env.NEXT_PUBLIC_PRO_MODE === '1';
export const IMAGE_QUALITY = export const IMAGE_QUALITY =
process.env.NEXT_PUBLIC_IMAGE_QUALITY process.env.NEXT_PUBLIC_IMAGE_QUALITY

View File

@ -1,4 +1,4 @@
import { SITE_DOMAIN_OR_TITLE } from '@/app/config'; import { NAV_TITLE_OR_DOMAIN } from '@/app/config';
import { Photo } from '../photo'; import { Photo } from '../photo';
import ImageCaption from './components/ImageCaption'; import ImageCaption from './components/ImageCaption';
import ImageContainer from './components/ImageContainer'; import ImageContainer from './components/ImageContainer';
@ -29,7 +29,7 @@ export default function HomeImageResponse({
width, width,
height, height,
fontFamily, fontFamily,
title: SITE_DOMAIN_OR_TITLE, title: NAV_TITLE_OR_DOMAIN,
}} /> }} />
</ImageContainer> </ImageContainer>
); );

View File

@ -10,7 +10,7 @@ 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, SITE_ABOUT } from '@/app/config'; import { CATEGORY_VISIBILITY, PAGE_ABOUT } from '@/app/config';
import { import {
htmlHasBrParagraphBreaks, htmlHasBrParagraphBreaks,
safelyParseFormattedHtml, safelyParseFormattedHtml,
@ -244,16 +244,16 @@ export default function PhotoGridSidebar({
return ( return (
<div className="space-y-4"> <div className="space-y-4">
{SITE_ABOUT && <HeaderList {PAGE_ABOUT && <HeaderList
items={[<p items={[<p
key="about" key="about"
ref={aboutRef} ref={aboutRef}
className={clsx( className={clsx(
'max-w-60 normal-case text-dim', 'max-w-60 normal-case text-dim',
htmlHasBrParagraphBreaks(SITE_ABOUT) && 'pb-2', htmlHasBrParagraphBreaks(PAGE_ABOUT) && 'pb-2',
)} )}
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: safelyParseFormattedHtml(SITE_ABOUT), __html: safelyParseFormattedHtml(PAGE_ABOUT),
}} }}
/>]} />]}
/>} />}