Leverage adjacent meta for about description
This commit is contained in:
parent
be859f380e
commit
8569347c27
@ -1,12 +1,11 @@
|
||||
import AdminAboutEditPage from '@/about/AdminAboutEditPage';
|
||||
import { getAbout } from '@/about/query';
|
||||
import { getAboutData } from '@/about/data';
|
||||
import { PRESERVE_ORIGINAL_UPLOADS } from '@/app/config';
|
||||
import { feedQueryOptions } from '@/feed';
|
||||
import {
|
||||
getPhotosCached,
|
||||
getPhotosMetaCached,
|
||||
} from '@/photo/cache';
|
||||
import { getPhoto } from '@/photo/query';
|
||||
import { TAG_FAVS } from '@/tag';
|
||||
|
||||
const PHOTO_CHOOSER_QUERY_OPTIONS = feedQueryOptions({
|
||||
@ -25,24 +24,7 @@ export default async function AboutEditPage() {
|
||||
photosCount,
|
||||
photosFavs,
|
||||
] = await Promise.all([
|
||||
getAbout()
|
||||
.then(async about => {
|
||||
const photoAvatar = about?.photoIdAvatar
|
||||
? await getPhoto(about?.photoIdAvatar ?? '', true)
|
||||
.catch(() => undefined)
|
||||
: undefined;
|
||||
|
||||
const photoHero = about?.photoIdHero
|
||||
? await getPhoto(about?.photoIdHero ?? '', true)
|
||||
.catch(() => undefined)
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
about,
|
||||
photoAvatar,
|
||||
photoHero,
|
||||
};
|
||||
})
|
||||
getAboutData()
|
||||
.catch(() => ({
|
||||
about: undefined,
|
||||
photoAvatar: undefined,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { getDescriptionWithFallback } from '@/about';
|
||||
import AboutPageClient from '@/about/AboutPageClient';
|
||||
import { getAboutCached } from '@/about/cache';
|
||||
import { getAboutDataCached } from '@/about/data';
|
||||
import { SHOW_ABOUT_PAGE } from '@/app/config';
|
||||
import { PATH_ROOT } from '@/app/path';
|
||||
import { getDataForCategoriesCached } from '@/category/cache';
|
||||
@ -7,11 +8,7 @@ import {
|
||||
getLastModifiedForCategories,
|
||||
NULL_CATEGORY_DATA,
|
||||
} from '@/category/data';
|
||||
import {
|
||||
getPhotoCached,
|
||||
getPhotosCached,
|
||||
getPhotosMetaCached,
|
||||
} from '@/photo/cache';
|
||||
import { getPhotosMetaCached } from '@/photo/cache';
|
||||
import PhotosEmptyState from '@/photo/PhotosEmptyState';
|
||||
import { getAllPhotoIdsWithUpdatedAt } from '@/photo/query';
|
||||
import { TAG_FAVS } from '@/tag';
|
||||
@ -34,26 +31,8 @@ export default async function AboutPage() {
|
||||
photos,
|
||||
categories,
|
||||
] = await Promise.all([
|
||||
getAboutCached()
|
||||
.then(async about => {
|
||||
const photoAvatar = await (about?.photoIdAvatar
|
||||
? getPhotoCached(about?.photoIdAvatar ?? '', true)
|
||||
: undefined);
|
||||
const photoHero = await (about?.photoIdHero
|
||||
? getPhotoCached(about?.photoIdHero ?? '', true)
|
||||
// Fall back to favorite photos if no hero photo is set
|
||||
: getPhotosCached({ tag: TAG_FAVS, limit: 1 })
|
||||
.then(photos => photos.length > 0
|
||||
? photos[0]
|
||||
// Fall back to oldest photo if no favorite photos exist
|
||||
: getPhotosCached({ limit: 1, sortBy: 'takenAtAsc' })
|
||||
.then(photos => photos[0])));
|
||||
return {
|
||||
about,
|
||||
photoAvatar,
|
||||
photoHero,
|
||||
};
|
||||
}).catch(() => ({
|
||||
getAboutDataCached()
|
||||
.catch(() => ({
|
||||
about: undefined,
|
||||
photoAvatar: undefined,
|
||||
photoHero: undefined,
|
||||
@ -63,10 +42,15 @@ export default async function AboutPage() {
|
||||
getDataForCategoriesCached().catch(() => (NULL_CATEGORY_DATA)),
|
||||
]);
|
||||
|
||||
const description = about?.description
|
||||
? <div dangerouslySetInnerHTML={{
|
||||
__html: safelyParseFormattedHtml(about.description),
|
||||
}} />
|
||||
const description = getDescriptionWithFallback(about);
|
||||
|
||||
const descriptionHtml = description
|
||||
? <div
|
||||
className="text-medium [&>*>a]:underline"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: safelyParseFormattedHtml(description),
|
||||
}}
|
||||
/>
|
||||
: undefined;
|
||||
|
||||
const {
|
||||
@ -88,7 +72,7 @@ export default async function AboutPage() {
|
||||
? <AboutPageClient
|
||||
title={about?.title}
|
||||
subhead={about?.subhead}
|
||||
description={description}
|
||||
descriptionHtml={descriptionHtml}
|
||||
photosCount={photosMeta?.count}
|
||||
photosOldest={photosMeta?.dateRange?.start}
|
||||
photoAvatar={photoAvatar}
|
||||
|
||||
@ -28,7 +28,7 @@ import AdminEmptyState from '@/admin/AdminEmptyState';
|
||||
export default function AboutPageClient({
|
||||
title,
|
||||
subhead,
|
||||
description,
|
||||
descriptionHtml,
|
||||
photosCount = 0,
|
||||
photosOldest,
|
||||
photoAvatar,
|
||||
@ -43,7 +43,7 @@ export default function AboutPageClient({
|
||||
}: {
|
||||
title?: string
|
||||
subhead?: string
|
||||
description?: ReactNode
|
||||
descriptionHtml?: ReactNode
|
||||
photosCount?: number
|
||||
photosOldest?: string
|
||||
photoAvatar?: Photo
|
||||
@ -178,26 +178,24 @@ export default function AboutPageClient({
|
||||
</div>
|
||||
{isUserSignedIn && <AdminAboutMenu />}
|
||||
</div>
|
||||
{description
|
||||
? <div className="text-medium [&>*>a]:underline">
|
||||
{description}
|
||||
</div>
|
||||
{descriptionHtml
|
||||
? descriptionHtml
|
||||
: isUserSignedIn &&
|
||||
<Link
|
||||
href={PATH_ADMIN_ABOUT_EDIT}
|
||||
className={clsx(
|
||||
'flex items-center justify-center gap-2.5',
|
||||
'border border-dashed border-medium rounded-lg',
|
||||
)}
|
||||
>
|
||||
<AdminEmptyState
|
||||
icon={<LuCirclePlus size={22} />}
|
||||
includeContainer={false}
|
||||
className="gap-3! p-6!"
|
||||
<Link
|
||||
href={PATH_ADMIN_ABOUT_EDIT}
|
||||
className={clsx(
|
||||
'flex items-center justify-center gap-2.5',
|
||||
'border border-dashed border-medium rounded-lg',
|
||||
)}
|
||||
>
|
||||
Add optional description
|
||||
</AdminEmptyState>
|
||||
</Link>}
|
||||
<AdminEmptyState
|
||||
icon={<LuCirclePlus size={22} />}
|
||||
includeContainer={false}
|
||||
className="gap-3! p-6!"
|
||||
>
|
||||
Add optional description
|
||||
</AdminEmptyState>
|
||||
</Link>}
|
||||
<AnimateItems
|
||||
className={clsx(
|
||||
'grid gap-x-2 gap-y-6 grid-cols-2 lg:grid-cols-4',
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import { PATH_ABOUT } from '@/app/path';
|
||||
import LinkWithStatus from '@/components/LinkWithStatus';
|
||||
import { useState } from 'react';
|
||||
import { About, AboutInsert } from '.';
|
||||
import { About, AboutInsert, getDescriptionWithFallback } from '.';
|
||||
import FieldsetWithStatus from '@/components/FieldsetWithStatus';
|
||||
import AdminChildPage from '@/components/AdminChildPage';
|
||||
import { updateAboutAction } from './actions';
|
||||
@ -32,8 +32,6 @@ export default function AdminAboutEditPage({
|
||||
|
||||
const [aboutForm, setAboutForm] = useState<Partial<AboutInsert>>(about ?? {});
|
||||
|
||||
const convertUrlToPhotoId = (url?: string) => url?.split('/').pop();
|
||||
|
||||
return (
|
||||
<AdminChildPage
|
||||
backPath={PATH_ABOUT}
|
||||
@ -48,7 +46,7 @@ export default function AdminAboutEditPage({
|
||||
<FieldsetPhotoChooser
|
||||
id="photoIdAvatar"
|
||||
label="Avatar"
|
||||
value={aboutForm?.photoIdAvatar ?? ''}
|
||||
value={aboutForm?.photoIdAvatar || photoAvatar?.id || ''}
|
||||
onChange={photoIdAvatar => setAboutForm(form =>
|
||||
({ ...form, photoIdAvatar }))}
|
||||
photo={photoAvatar}
|
||||
@ -74,15 +72,16 @@ export default function AdminAboutEditPage({
|
||||
label="Description"
|
||||
type="textarea"
|
||||
value={aboutForm?.description ?? ''}
|
||||
placeholder={getDescriptionWithFallback(about)}
|
||||
onChange={description => setAboutForm(form =>
|
||||
({ ...form, description }))}
|
||||
/>
|
||||
<FieldsetPhotoChooser
|
||||
id="photoIdHero"
|
||||
label="Hero"
|
||||
value={aboutForm?.photoIdHero ?? ''}
|
||||
value={aboutForm?.photoIdHero || photoHero?.id || ''}
|
||||
onChange={photoIdHero => setAboutForm(form =>
|
||||
({ ...form, photoIdHero: convertUrlToPhotoId(photoIdHero) }))}
|
||||
({ ...form, photoIdHero }))}
|
||||
photo={photoHero}
|
||||
photos={photos}
|
||||
photosCount={photosCount}
|
||||
|
||||
37
src/about/data.ts
Normal file
37
src/about/data.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { getPhotoCached, getPhotosCached } from '@/photo/cache';
|
||||
import { About } from '.';
|
||||
import { TAG_FAVS } from '@/tag';
|
||||
import { getAbout } from './query';
|
||||
import { getAboutCached } from './cache';
|
||||
|
||||
const getAboutAvatar = (about?: About) =>
|
||||
about?.photoIdAvatar
|
||||
? getPhotoCached(about?.photoIdAvatar ?? '', true)
|
||||
: undefined;
|
||||
|
||||
const getAboutHero = (about?: About) =>
|
||||
about?.photoIdHero
|
||||
? getPhotoCached(about?.photoIdHero ?? '', true)
|
||||
// Fall back to favorite photos if no hero photo is set
|
||||
: getPhotosCached({ tag: TAG_FAVS, limit: 1 })
|
||||
.then(photos => photos.length > 0
|
||||
? photos[0]
|
||||
// Fall back to oldest photo if no favorite photos exist
|
||||
: getPhotosCached({ limit: 1, sortBy: 'takenAtAsc' })
|
||||
.then(photos => photos[0]));
|
||||
|
||||
export const getAboutData = () =>
|
||||
getAbout()
|
||||
.then(async about => ({
|
||||
about,
|
||||
photoAvatar: await getAboutAvatar(about),
|
||||
photoHero: await getAboutHero(about),
|
||||
}));
|
||||
|
||||
export const getAboutDataCached = () =>
|
||||
getAboutCached()
|
||||
.then(async about => ({
|
||||
about,
|
||||
photoAvatar: await getAboutAvatar(about),
|
||||
photoHero: await getAboutHero(about),
|
||||
}));
|
||||
@ -1,3 +1,5 @@
|
||||
import { META_DESCRIPTION, SIDEBAR_TEXT } from '@/app/config';
|
||||
|
||||
export interface AboutInsert {
|
||||
id: number
|
||||
title?: string
|
||||
@ -11,3 +13,8 @@ export interface About extends AboutInsert {
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
export const getDescriptionWithFallback = (about?: About) =>
|
||||
about?.description ||
|
||||
META_DESCRIPTION ||
|
||||
SIDEBAR_TEXT;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user