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 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;
|
||||||
|
|
|
@ -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']}>
|
||||||
|
|
|
@ -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`
|
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 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';
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue