feat: metadata for blog posts
Signed-off-by: Filipe Medeiros <hello@filipesm.eu>
This commit is contained in:
parent
743424d84c
commit
9112897833
Binary file not shown.
30
frontend/src/cms/queries/blogPosts.ts
Normal file
30
frontend/src/cms/queries/blogPosts.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import getMostRecentRevisions from '../../lib/cms/getMostRecentRevisions';
|
||||
import groq from 'groq';
|
||||
|
||||
export const forBlogPostPage = groq`
|
||||
${getMostRecentRevisions(groq`_type == "blogPost"`)}
|
||||
{
|
||||
"slug": slug.current,
|
||||
title,
|
||||
content[]{
|
||||
_type == 'image' => {
|
||||
...,
|
||||
"lqip": @.asset->metadata.lqip,
|
||||
"alt": @.asset->altText,
|
||||
"aspectRatio": @.asset->metadata.dimensions.aspectRatio
|
||||
},
|
||||
_type != 'image' => @
|
||||
},
|
||||
headerImage,
|
||||
publishDate
|
||||
}`;
|
||||
|
||||
export const forBlogPage = groq`
|
||||
${getMostRecentRevisions(groq`_type == "blogPost"`)}
|
||||
{
|
||||
title,
|
||||
summary,
|
||||
publishDate,
|
||||
"slug": slug.current
|
||||
}
|
||||
`;
|
18
frontend/src/components/BlogPostMetadata.astro
Normal file
18
frontend/src/components/BlogPostMetadata.astro
Normal file
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
import type { ImageObject } from '../lib/cms/types';
|
||||
import urlFor from '../lib/cms/urlFor';
|
||||
|
||||
export interface Props {
|
||||
description: string;
|
||||
image: ImageObject;
|
||||
twitterCardType: 'summary' | 'summary_large_image' | 'player' | 'app';
|
||||
title?: string;
|
||||
}
|
||||
|
||||
const { description, image, twitterCardType, title } = Astro.props;
|
||||
---
|
||||
|
||||
<meta name="description" content={description} />
|
||||
<meta name="og:image" content={urlFor(image).width(1200).height(630).url()} />
|
||||
<meta name="twitter:card" content={twitterCardType} />
|
||||
{title && <meta name="og:title" content={title} />}
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
import type { PortableTextBlock } from '@portabletext/types';
|
||||
import urlFor from '../../../../lib/cms/urlFor';
|
||||
import urlFor from '../../../lib/cms/urlFor';
|
||||
|
||||
export interface Props {
|
||||
node: BlogPostContentImage;
|
||||
|
|
|
@ -13,9 +13,10 @@ const hasFooter = !!footer;
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" type="image/favicon" href="/favicon.ico" />
|
||||
<title>{title}</title>
|
||||
<slot name="metadata" />
|
||||
</head>
|
||||
<body class="bg-orange-50 font-serif">
|
||||
<main class:list={['px-6 pt-4 md:px-20 md:pt-6', hasFooter && 'mb-32']}>
|
||||
|
|
|
@ -16,7 +16,9 @@ export const forBlogPostPage = groq`
|
|||
_type != 'image' => @
|
||||
},
|
||||
headerImage,
|
||||
publishDate
|
||||
publishDate,
|
||||
summary,
|
||||
metadata
|
||||
}`;
|
||||
|
||||
export const forBlogPage = groq`
|
5
frontend/src/lib/cms/types.ts
Normal file
5
frontend/src/lib/cms/types.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export interface ImageObject {
|
||||
_type: 'image';
|
||||
_key: string;
|
||||
asset: { _type: 'reference'; ref: string };
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
import client from '../../../lib/cms/client';
|
||||
import { forLibraryPage } from '../../../lib/cms/queries/libraryItems';
|
||||
import client from '../../lib/cms/client';
|
||||
import { forLibraryPage } from '../../lib/cms/queries/libraryItems';
|
||||
import ButtonLink from '../../components/ButtonLink.astro';
|
||||
import CardLink from '../../components/CardLink.astro';
|
||||
import PageTitle from '../../components/PageTitle.astro';
|
||||
|
|
|
@ -1,11 +1,27 @@
|
|||
---
|
||||
import type { PortableTextBlock } from '@portabletext/types';
|
||||
import client from '../../../../lib/cms/client';
|
||||
import { forBlogPostPage } from '../../../../lib/cms/queries/blogPosts';
|
||||
import client from '../../../lib/cms/client';
|
||||
import { forBlogPostPage } from '../../../lib/cms/queries/blogPosts';
|
||||
import BlogPostContent from '../../../components/portableText/BlogPostContent.astro';
|
||||
import ButtonLink from '../../../components/ButtonLink.astro';
|
||||
import PageTitle from '../../../components/PageTitle.astro';
|
||||
import Layout from '../../../layouts/Layout.astro';
|
||||
import BlogPostMetadata from '../../../components/BlogPostMetadata.astro';
|
||||
import type { ImageObject } from '../../../lib/cms/types';
|
||||
|
||||
export interface Props {
|
||||
title: string;
|
||||
content: PortableTextBlock[];
|
||||
publishDate: string;
|
||||
headerImage: ImageObject;
|
||||
summary: string;
|
||||
metadata: {
|
||||
twitterCardType: 'summary' | 'summary_large_image' | 'player' | 'app';
|
||||
description: string;
|
||||
title: string;
|
||||
image: ImageObject;
|
||||
};
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts: {
|
||||
|
@ -13,20 +29,33 @@ export async function getStaticPaths() {
|
|||
title: string;
|
||||
content: PortableTextBlock[];
|
||||
publishDate: string;
|
||||
headerImage: ImageObject;
|
||||
summary: string;
|
||||
metadata: Props['metadata'];
|
||||
}[] = await client.fetch(forBlogPostPage, { live: !import.meta.env.DEV });
|
||||
|
||||
return posts.map(({ slug, title, content, publishDate }) => {
|
||||
return {
|
||||
params: { slug },
|
||||
props: { title, content, publishDate },
|
||||
};
|
||||
});
|
||||
return posts.map(
|
||||
({ slug, title, content, publishDate, headerImage, summary, metadata }) => {
|
||||
return {
|
||||
params: { slug },
|
||||
props: { title, content, publishDate, headerImage, summary, metadata },
|
||||
};
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const { title, content, publishDate } = Astro.props;
|
||||
const { title, content, publishDate, headerImage, summary, metadata } =
|
||||
Astro.props;
|
||||
---
|
||||
|
||||
<Layout title={`${title} - Filipe Medeiros`}>
|
||||
<BlogPostMetadata
|
||||
slot="metadata"
|
||||
title={metadata.title ?? title}
|
||||
description={metadata.description ?? summary}
|
||||
image={metadata.image ?? headerImage}
|
||||
twitterCardType={metadata.twitterCardType}
|
||||
/>
|
||||
<article class="max-w-prose m-auto">
|
||||
<header class="mb-5">
|
||||
<PageTitle class="mb-2 break-words">{title}</PageTitle>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
---
|
||||
import client from '../../../lib/cms/client';
|
||||
import { forBlogPage } from '../../../lib/cms/queries/blogPosts';
|
||||
import client from '../../lib/cms/client';
|
||||
import { forBlogPage } from '../../lib/cms/queries/blogPosts';
|
||||
import ButtonLink from '../../components/ButtonLink.astro';
|
||||
import CardLink from '../../components/CardLink.astro';
|
||||
import PageTitle from '../../components/PageTitle.astro';
|
||||
import Layout from '../../layouts/Layout.astro';
|
||||
import type { ImageObject } from '../../lib/cms/types';
|
||||
|
||||
const posts: {
|
||||
title: string;
|
||||
|
|
Loading…
Reference in a new issue