feat(SW-200): Replaced Contentstack image with Imagevault image for metadata

This commit is contained in:
Erik Tiekstra
2024-11-15 14:05:59 +01:00
parent 50f285776a
commit 956d031ffc
5 changed files with 137 additions and 109 deletions

View File

@@ -1,30 +1,6 @@
fragment MetadataImageConnection on SeoMetadata {
imageConnection {
edges {
node {
dimension {
height
width
}
url(transform: { width: "1200" })
}
}
}
}
fragment Metadata on SeoMetadata {
noindex
description
title
imageConnection {
edges {
node {
dimension {
height
width
}
url(transform: { width: "1200" })
}
}
}
seo_image
}

View File

@@ -15,7 +15,7 @@ query GetLoyaltyPageMetadata($locale: String!, $uid: String!) {
preamble
hero_image
blocks {
... on ContentPageBlocksContent {
... on LoyaltyPageBlocksContent {
__typename
content {
content {

View File

@@ -7,39 +7,59 @@ import type { Metadata } from "next"
import { RTETypeEnum } from "@/types/rte/enums"
const metaDataJsonSchema = z.object({
children: z.array(
z.object({
type: z.nativeEnum(RTETypeEnum),
children: z.array(
z.object({
text: z.string().optional(),
})
),
})
),
})
const metaDataBlocksSchema = z
.array(
z.object({
content: z
.object({
content: z
.object({
json: metaDataJsonSchema,
})
.optional()
.nullable(),
})
.optional()
.nullable(),
})
)
.optional()
.nullable()
export const rawMetadataSchema = z.object({
web: z.object({
seo_metadata: z
.object({
title: z.string().optional().nullable(),
description: z.string().optional().nullable(),
noindex: z.boolean().optional().nullable(),
imageConnection: z
.object({
edges: z.array(
z.object({
node: z.object({
url: z.string(),
dimension: z.object({
width: z.number(),
height: z.number(),
}),
}),
})
),
})
.optional()
.nullable(),
})
.optional()
.nullable(),
breadcrumbs: z
.object({
title: z.string().optional().nullable(),
})
.optional()
.nullable(),
}),
web: z
.object({
seo_metadata: z
.object({
title: z.string().optional().nullable(),
description: z.string().optional().nullable(),
noindex: z.boolean().optional().nullable(),
seo_image: tempImageVaultAssetSchema.nullable(),
})
.optional()
.nullable(),
breadcrumbs: z
.object({
title: z.string().optional().nullable(),
})
.optional()
.nullable(),
})
.optional()
.nullable(),
heading: z.string().optional().nullable(),
preamble: z.string().optional().nullable(),
header: z
@@ -49,49 +69,26 @@ export const rawMetadataSchema = z.object({
})
.optional()
.nullable(),
hero_image: tempImageVaultAssetSchema,
blocks: z
.array(
z.object({
content: z
.object({
content: z
.object({
json: z.object({
children: z.array(
z.object({
type: z.nativeEnum(RTETypeEnum),
children: z.array(
z.object({
text: z.string().optional().nullable(),
})
),
})
),
}),
})
.optional()
.nullable(),
})
.optional()
.nullable(),
})
)
.optional()
.nullable(),
hero_image: tempImageVaultAssetSchema.nullable(),
blocks: metaDataBlocksSchema,
})
export const metadataSchema = rawMetadataSchema.transform((data) => {
const noIndex = !!data.web?.seo_metadata?.noindex
const metadata: Metadata = {
robots: {
index: !data.web.seo_metadata?.noindex,
follow: true,
},
title: getTitle(data),
description: getDescription(data),
openGraph: {
images: getImages(data),
},
}
if (noIndex) {
metadata.robots = {
index: false,
follow: true,
}
}
return metadata
})

View File

@@ -3,8 +3,63 @@ import type { RawMetadataSchema } from "@/types/trpc/routers/contentstack/metada
export const affix = "metadata"
/**
* Truncates the given text "intelligently" based on the last period found near the max length.
*
* - If a period exists within the extended range (`maxLength` to `maxLength + maxExtension`),
* the function truncates after the closest period to `maxLength`.
* - If no period is found in the range, it truncates the text after the last period found in the max length of the text.
* - If no periods exist at all, it truncates at `maxLength` and appends ellipsis (`...`).
*
* @param {string} text - The input text to be truncated.
* @param {number} [maxLength=150] - The desired maximum length of the truncated text.
* @param {number} [minLength=120] - The minimum allowable length for the truncated text.
* @param {number} [maxExtension=10] - The maximum number of characters to extend beyond `maxLength` to find a period.
* @returns {string} - The truncated text.
*/
function truncateTextAfterLastPeriod(
text: string,
maxLength: number = 150,
minLength: number = 120,
maxExtension: number = 10
): string {
if (text.length <= maxLength) {
return text
}
// Define the extended range
const extendedEnd = Math.min(text.length, maxLength + maxExtension)
const extendedText = text.slice(0, extendedEnd)
// Find all periods within the extended range and filter after minLength to get valid periods
const periodsInRange = [...extendedText.matchAll(/\./g)].map(
({ index }) => index
)
const validPeriods = periodsInRange.filter((index) => index + 1 >= minLength)
if (validPeriods.length > 0) {
// Find the period closest to maxLength
const closestPeriod = validPeriods.reduce((closest, index) =>
Math.abs(index + 1 - maxLength) < Math.abs(closest + 1 - maxLength)
? index
: closest
)
return extendedText.slice(0, closestPeriod + 1)
}
// Fallback: If no period is found within the valid range, look for the last period in the truncated text
const maxLengthText = text.slice(0, maxLength)
const lastPeriodIndex = maxLengthText.lastIndexOf(".")
if (lastPeriodIndex !== -1) {
return text.slice(0, lastPeriodIndex + 1)
}
// Final fallback: Return maxLength text including ellipsis
return `${maxLengthText}...`
}
export function getTitle(data: RawMetadataSchema) {
const metadata = data.web.seo_metadata
const metadata = data.web?.seo_metadata
if (metadata?.title) {
return metadata.title
}
@@ -21,15 +76,15 @@ export function getTitle(data: RawMetadataSchema) {
}
export function getDescription(data: RawMetadataSchema) {
const metadata = data.web.seo_metadata
const metadata = data.web?.seo_metadata
if (metadata?.description) {
return metadata.description
}
if (data.preamble) {
return data.preamble
return truncateTextAfterLastPeriod(data.preamble)
}
if (data.header?.preamble) {
return data.header.preamble
return truncateTextAfterLastPeriod(data.header.preamble)
}
if (data.blocks?.length) {
const jsonData = data.blocks[0].content?.content?.json
@@ -40,24 +95,26 @@ export function getDescription(data: RawMetadataSchema) {
if (firstParagraph?.children?.length) {
return firstParagraph.children[0].text
? truncateTextAfterLastPeriod(firstParagraph.children[0].text)
: ""
}
}
return ""
}
export function getImages(data: RawMetadataSchema) {
const metadataImages = data.web.seo_metadata?.imageConnection?.edges
const metadataImage = data.web?.seo_metadata?.seo_image
const heroImage = data.hero_image
if (metadataImages?.length) {
return metadataImages.map((edge) => {
const { width, height } = edge.node.dimension
return {
url: edge.node.url,
width: 1200,
height: Math.round((1200 * height) / width),
}
})
// Currently we don't have the possibility to get smaller images from ImageVault (2024-11-15)
if (metadataImage) {
return [
{
url: metadataImage.url,
width: metadataImage.dimensions.width,
height: metadataImage.dimensions.height,
},
]
}
if (heroImage) {
return [

View File

@@ -1,7 +1,5 @@
import { serverClient } from "@/lib/trpc/server"
export async function generateMetadata() {
const data = await serverClient().contentstack.metadata.get()
return data
return await serverClient().contentstack.metadata.get()
}