feat: metadata for blog posts

Signed-off-by: Filipe Medeiros <hello@filipesm.eu>
This commit is contained in:
Filipe Medeiros 2023-04-11 20:32:13 +01:00
parent 743424d84c
commit 9112897833
Signed by: filipe
GPG key ID: 9533BD5467CC1E78
14 changed files with 102 additions and 16 deletions

Binary file not shown.

View 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
}
`;

View 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} />}

View file

@ -1,6 +1,6 @@
--- ---
import type { PortableTextBlock } from '@portabletext/types'; import type { PortableTextBlock } from '@portabletext/types';
import urlFor from '../../../../lib/cms/urlFor'; import urlFor from '../../../lib/cms/urlFor';
export interface Props { export interface Props {
node: BlogPostContentImage; node: BlogPostContentImage;

View file

@ -13,9 +13,10 @@ const hasFooter = !!footer;
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <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" /> <link rel="icon" type="image/favicon" href="/favicon.ico" />
<title>{title}</title> <title>{title}</title>
<slot name="metadata" />
</head> </head>
<body class="bg-orange-50 font-serif"> <body class="bg-orange-50 font-serif">
<main class:list={['px-6 pt-4 md:px-20 md:pt-6', hasFooter && 'mb-32']}> <main class:list={['px-6 pt-4 md:px-20 md:pt-6', hasFooter && 'mb-32']}>

View file

@ -16,7 +16,9 @@ export const forBlogPostPage = groq`
_type != 'image' => @ _type != 'image' => @
}, },
headerImage, headerImage,
publishDate publishDate,
summary,
metadata
}`; }`;
export const forBlogPage = groq` export const forBlogPage = groq`

View file

@ -0,0 +1,5 @@
export interface ImageObject {
_type: 'image';
_key: string;
asset: { _type: 'reference'; ref: string };
}

View file

@ -1,6 +1,6 @@
--- ---
import client from '../../../lib/cms/client'; import client from '../../lib/cms/client';
import { forLibraryPage } from '../../../lib/cms/queries/libraryItems'; import { forLibraryPage } from '../../lib/cms/queries/libraryItems';
import ButtonLink from '../../components/ButtonLink.astro'; import ButtonLink from '../../components/ButtonLink.astro';
import CardLink from '../../components/CardLink.astro'; import CardLink from '../../components/CardLink.astro';
import PageTitle from '../../components/PageTitle.astro'; import PageTitle from '../../components/PageTitle.astro';

View file

@ -1,11 +1,27 @@
--- ---
import type { PortableTextBlock } from '@portabletext/types'; import type { PortableTextBlock } from '@portabletext/types';
import client from '../../../../lib/cms/client'; import client from '../../../lib/cms/client';
import { forBlogPostPage } from '../../../../lib/cms/queries/blogPosts'; import { forBlogPostPage } from '../../../lib/cms/queries/blogPosts';
import BlogPostContent from '../../../components/portableText/BlogPostContent.astro'; import BlogPostContent from '../../../components/portableText/BlogPostContent.astro';
import ButtonLink from '../../../components/ButtonLink.astro'; import ButtonLink from '../../../components/ButtonLink.astro';
import PageTitle from '../../../components/PageTitle.astro'; import PageTitle from '../../../components/PageTitle.astro';
import Layout from '../../../layouts/Layout.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() { export async function getStaticPaths() {
const posts: { const posts: {
@ -13,20 +29,33 @@ export async function getStaticPaths() {
title: string; title: string;
content: PortableTextBlock[]; content: PortableTextBlock[];
publishDate: string; publishDate: string;
headerImage: ImageObject;
summary: string;
metadata: Props['metadata'];
}[] = await client.fetch(forBlogPostPage, { live: !import.meta.env.DEV }); }[] = await client.fetch(forBlogPostPage, { live: !import.meta.env.DEV });
return posts.map(({ slug, title, content, publishDate }) => { return posts.map(
return { ({ slug, title, content, publishDate, headerImage, summary, metadata }) => {
params: { slug }, return {
props: { title, content, publishDate }, 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`}> <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"> <article class="max-w-prose m-auto">
<header class="mb-5"> <header class="mb-5">
<PageTitle class="mb-2 break-words">{title}</PageTitle> <PageTitle class="mb-2 break-words">{title}</PageTitle>

View file

@ -1,10 +1,11 @@
--- ---
import client from '../../../lib/cms/client'; import client from '../../lib/cms/client';
import { forBlogPage } from '../../../lib/cms/queries/blogPosts'; import { forBlogPage } from '../../lib/cms/queries/blogPosts';
import ButtonLink from '../../components/ButtonLink.astro'; import ButtonLink from '../../components/ButtonLink.astro';
import CardLink from '../../components/CardLink.astro'; import CardLink from '../../components/CardLink.astro';
import PageTitle from '../../components/PageTitle.astro'; import PageTitle from '../../components/PageTitle.astro';
import Layout from '../../layouts/Layout.astro'; import Layout from '../../layouts/Layout.astro';
import type { ImageObject } from '../../lib/cms/types';
const posts: { const posts: {
title: string; title: string;