2023-02-16 22:39:57 +00:00
import type { NextSeoProps } from "next-seo" ;
import { NextSeo } from "next-seo" ;
2023-08-02 09:35:48 +00:00
import { usePathname } from "next/navigation" ;
2022-10-18 17:46:22 +00:00
2023-02-16 22:39:57 +00:00
import type { AppImageProps , MeetingImageProps } from "@calcom/lib/OgImages" ;
import { constructAppImage , constructGenericImage , constructMeetingImage } from "@calcom/lib/OgImages" ;
2023-07-10 14:10:30 +00:00
import { APP_NAME , WEBSITE_URL , IS_CALCOM } from "@calcom/lib/constants" ;
2023-06-02 18:28:03 +00:00
import { seoConfig , getSeoImage , buildCanonical } from "@calcom/lib/next-seo.config" ;
2022-11-08 07:36:59 +00:00
import { truncateOnWord } from "@calcom/lib/text" ;
2021-09-22 19:52:38 +00:00
2021-08-27 12:35:20 +00:00
export type HeadSeoProps = {
title : string ;
description : string ;
siteName? : string ;
url? : string ;
canonical? : string ;
nextSeoProps? : NextSeoProps ;
2022-10-18 17:46:22 +00:00
app? : AppImageProps ;
meeting? : MeetingImageProps ;
2023-03-20 11:41:03 +00:00
isBrandingHidden? : boolean ;
2021-08-27 12:35:20 +00:00
} ;
/ * *
* Build full seo tags from title , desc , canonical and url
* /
const buildSeoMeta = ( pageProps : {
title : string ;
description : string ;
image : string ;
siteName? : string ;
url? : string ;
canonical? : string ;
2023-08-18 18:13:21 +00:00
} ) = > {
2021-08-27 12:35:20 +00:00
const { title , description , image , canonical , siteName = seoConfig . headSeo . siteName } = pageProps ;
return {
title : title ,
canonical : canonical ,
openGraph : {
site_name : siteName ,
type : "website" ,
title : title ,
description : description ,
images : [
{
url : image ,
} ,
] ,
} ,
additionalMetaTags : [
{
property : "name" ,
content : title ,
} ,
{
property : "description" ,
content : description ,
} ,
{
name : "description" ,
content : description ,
} ,
{
property : "image" ,
content : image ,
} ,
] ,
} ;
} ;
2022-04-04 20:26:14 +00:00
export const HeadSeo = ( props : HeadSeoProps ) : JSX . Element = > {
2023-08-02 09:35:48 +00:00
const path = usePathname ( ) ;
2023-01-31 19:07:50 +00:00
// The below code sets the defaultUrl for our canonical tags
// Get the router's path
2023-06-02 18:28:03 +00:00
const selfHostedOrigin = WEBSITE_URL || "https://cal.com" ;
2023-01-31 19:07:50 +00:00
// Set the default URL to either the current URL (if self-hosted) or https://cal.com canonical URL
2023-07-10 14:10:30 +00:00
const defaultUrl = IS_CALCOM
2023-06-02 18:28:03 +00:00
? buildCanonical ( { path , origin : "https://cal.com" } )
: buildCanonical ( { path , origin : selfHostedOrigin } ) ;
2021-08-27 12:35:20 +00:00
2023-03-20 11:41:03 +00:00
const {
title ,
description ,
siteName ,
canonical = defaultUrl ,
nextSeoProps = { } ,
app ,
meeting ,
isBrandingHidden ,
} = props ;
2022-10-18 17:46:22 +00:00
2022-11-01 08:16:08 +00:00
const image = getSeoImage ( "ogImage" ) + constructGenericImage ( { title , description } ) ;
const truncatedDescription = truncateOnWord ( description , 158 ) ;
2023-03-20 11:41:03 +00:00
const pageTitle = ` ${ title } ${ isBrandingHidden ? "" : ` | ${ APP_NAME } ` } ` ;
2022-01-11 08:54:02 +00:00
let seoObject = buildSeoMeta ( {
title : pageTitle ,
image ,
description : truncatedDescription ,
canonical ,
siteName ,
} ) ;
2021-08-27 12:35:20 +00:00
2022-10-18 17:46:22 +00:00
if ( meeting ) {
const pageImage = getSeoImage ( "ogImage" ) + constructMeetingImage ( meeting ) ;
seoObject = buildSeoMeta ( {
title : pageTitle ,
description : truncatedDescription ,
image : pageImage ,
canonical ,
siteName ,
} ) ;
}
if ( app ) {
const pageImage =
2022-11-01 08:16:08 +00:00
getSeoImage ( "ogImage" ) + constructAppImage ( { . . . app , description : truncatedDescription } ) ;
2022-01-11 08:54:02 +00:00
seoObject = buildSeoMeta ( {
title : pageTitle ,
description : truncatedDescription ,
image : pageImage ,
canonical ,
siteName ,
} ) ;
2021-08-27 12:35:20 +00:00
}
2023-08-18 18:13:21 +00:00
// Instead of doing a blackbox deep merge which can be tricky implementation wise and need a good implementation, we should generate the object manually as we know the properties
// Goal is to avoid big dependency
const seoProps : NextSeoProps = {
. . . nextSeoProps ,
. . . seoObject ,
openGraph : {
. . . nextSeoProps . openGraph ,
. . . seoObject . openGraph ,
images : [ . . . ( nextSeoProps . openGraph ? . images || [ ] ) , . . . seoObject . openGraph . images ] ,
} ,
additionalMetaTags : [ . . . ( nextSeoProps . additionalMetaTags || [ ] ) ] ,
} ;
2021-08-27 12:35:20 +00:00
return < NextSeo { ...seoProps } / > ;
} ;
2022-04-04 20:26:14 +00:00
export default HeadSeo ;