Improve Open Graph Metas
This commit is contained in:
		
							parent
							
								
									d5e7d704bf
								
							
						
					
					
						commit
						ff89031123
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,6 +1,8 @@ | ||||
| # Generated content | ||||
| src/graphql/generated.ts | ||||
| 
 | ||||
| public/robots.txt | ||||
| 
 | ||||
| # dependencies | ||||
| /node_modules | ||||
| /.pnp | ||||
|  | ||||
| @ -2,6 +2,7 @@ import Head from "next/head"; | ||||
| import { useSwipeable } from "react-swipeable"; | ||||
| import { MaterialSymbol } from "material-symbols"; | ||||
| import { atom } from "jotai"; | ||||
| import { useRouter } from "next/router"; | ||||
| import { layout } from "../../design.config"; | ||||
| import { Ico } from "./Ico"; | ||||
| import { MainPanel } from "./Panels/MainPanel"; | ||||
| @ -54,6 +55,7 @@ export const AppLayout = ({ | ||||
|   const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout); | ||||
|   const isScreenAtLeastXs = useAtomGetter(atoms.containerQueries.isScreenAtLeastXs); | ||||
|   const isIOS = useAtomGetter(isIOSAtom); | ||||
|   const router = useRouter(); | ||||
| 
 | ||||
|   const { format } = useFormat(); | ||||
| 
 | ||||
| @ -105,19 +107,41 @@ export const AppLayout = ({ | ||||
|         <title>{openGraph.title}</title> | ||||
|         <meta name="description" content={openGraph.description} /> | ||||
| 
 | ||||
|         <meta name="twitter:site" content="@AccordsLibrary" /> | ||||
|         <meta name="twitter:title" content={openGraph.title} /> | ||||
|         <meta name="twitter:description" content={openGraph.description} /> | ||||
|         <meta name="twitter:card" content="summary_large_image" /> | ||||
|         <meta name="twitter:image" content={openGraph.thumbnail.image} /> | ||||
| 
 | ||||
|         <meta | ||||
|           property="og:type" | ||||
|           content={openGraph.video ? "video.movie" : openGraph.audio ? "music.song" : "website"} | ||||
|         /> | ||||
|         <meta property="og:locale" content={router.locale} /> | ||||
|         <meta property="og:site_name" content="Accord’s Library" /> | ||||
| 
 | ||||
|         <meta property="og:title" content={openGraph.title} /> | ||||
|         <meta property="og:description" content={openGraph.description} /> | ||||
| 
 | ||||
|         <meta property="og:image" content={openGraph.thumbnail.image} /> | ||||
|         <meta property="og:image:secure_url" content={openGraph.thumbnail.image} /> | ||||
|         <meta property="og:image:width" content={openGraph.thumbnail.width.toString()} /> | ||||
|         <meta property="og:image:height" content={openGraph.thumbnail.height.toString()} /> | ||||
|         <meta property="og:image:alt" content={openGraph.thumbnail.alt} /> | ||||
|         <meta property="og:image:type" content="image/jpeg" /> | ||||
| 
 | ||||
|         {openGraph.audio && ( | ||||
|           <> | ||||
|             <meta property="og:audio" content={openGraph.audio} /> | ||||
|             <meta property="og:audio:type" content="audio/mpeg" /> | ||||
|           </> | ||||
|         )} | ||||
|         {openGraph.video && ( | ||||
|           <> | ||||
|             <meta property="og:video" content={openGraph.video} />{" "} | ||||
|             <meta property="og:video:type" content="video/mp4" /> | ||||
|           </> | ||||
|         )} | ||||
|       </Head> | ||||
| 
 | ||||
|       {/* Content panel */} | ||||
|  | ||||
| @ -8,7 +8,7 @@ const DEFAULT_OG_THUMBNAIL: OgImage = { | ||||
|   image: `${process.env.NEXT_PUBLIC_URL_SELF}/default_og.jpg`, | ||||
|   width: 1200, | ||||
|   height: 630, | ||||
|   alt: "Accord's Library Logo", | ||||
|   alt: "Accord’s Library Logo", | ||||
| }; | ||||
| 
 | ||||
| export const TITLE_PREFIX = "Accord’s Library"; | ||||
| @ -18,13 +18,17 @@ export interface OpenGraph { | ||||
|   title: string; | ||||
|   description: string; | ||||
|   thumbnail: OgImage; | ||||
|   audio?: string; | ||||
|   video?: string; | ||||
| } | ||||
| 
 | ||||
| export const getOpenGraph = ( | ||||
|   format: ReturnType<typeof getFormat>["format"] | ReturnType<typeof useFormat>["format"], | ||||
|   title?: string | null | undefined, | ||||
|   description?: string | null | undefined, | ||||
|   thumbnail?: UploadImageFragment | string | null | undefined | ||||
|   thumbnail?: UploadImageFragment | string | null | undefined, | ||||
|   audio?: string, | ||||
|   video?: string | ||||
| ): OpenGraph => ({ | ||||
|   title: `${TITLE_PREFIX}${isDefinedAndNotEmpty(title) ? `${TITLE_SEPARATOR}${title}` : ""}`, | ||||
|   description: isDefinedAndNotEmpty(description) | ||||
| @ -33,6 +37,8 @@ export const getOpenGraph = ( | ||||
|       : description | ||||
|     : format("default_description"), | ||||
|   thumbnail: thumbnail ? getOgImage(thumbnail) : DEFAULT_OG_THUMBNAIL, | ||||
|   ...(audio ? { audio } : {}), | ||||
|   ...(video ? { video } : {}), | ||||
| }); | ||||
| 
 | ||||
| const getOgImage = (image: UploadImageFragment | string): OgImage => { | ||||
|  | ||||
| @ -167,7 +167,11 @@ export const getStaticProps: GetStaticProps = async (context) => { | ||||
|       getDescription(videos.videos.data[0].attributes.description, { | ||||
|         [format("channel")]: [videos.videos.data[0].attributes.channel?.data?.attributes?.title], | ||||
|       }), | ||||
|       getVideoThumbnailURL(videos.videos.data[0].attributes.uid) | ||||
|       getVideoThumbnailURL(videos.videos.data[0].attributes.uid), | ||||
|       undefined, | ||||
|       videos.videos.data[0].attributes.gone | ||||
|         ? getVideoFile(videos.videos.data[0].attributes.uid) | ||||
|         : undefined | ||||
|     ), | ||||
|   }; | ||||
|   return { | ||||
|  | ||||
| @ -275,30 +275,31 @@ const Content = ({ content, ...otherProps }: Props): JSX.Element => { | ||||
| 
 | ||||
|             selectedSetType === "text_set" && selectedTranslation?.text_set?.text ? ( | ||||
|               <Markdawn className="max-w-2xl" text={selectedTranslation.text_set.text} /> | ||||
|             ) : selectedSetType === "audio_set" && selectedTranslation?.audio_set ? ( | ||||
|             ) : selectedSetType === "audio_set" && | ||||
|               selectedTranslation?.audio_set && | ||||
|               selectedTranslation.language?.data?.attributes?.code ? ( | ||||
|               <AudioPlayer | ||||
|                 title={prettyInlineTitle( | ||||
|                   selectedTranslation.pre_title, | ||||
|                   selectedTranslation.title, | ||||
|                   selectedTranslation.subtitle | ||||
|                 )} | ||||
|                 src={`${process.env.NEXT_PUBLIC_URL_ASSETS}/contents/audios/\ | ||||
| ${content.slug}_${selectedTranslation.language?.data?.attributes?.code}.mp3`}
 | ||||
|                 src={getAudioURL(content.slug, selectedTranslation.language.data.attributes.code)} | ||||
|                 className="max-w-2xl" | ||||
|               /> | ||||
|             ) : ( | ||||
|               selectedSetType === "video_set" && | ||||
|               selectedTranslation?.video_set && ( | ||||
|               selectedTranslation?.video_set && | ||||
|               selectedTranslation.language?.data?.attributes?.code && ( | ||||
|                 <VideoPlayer | ||||
|                   title={prettyInlineTitle( | ||||
|                     selectedTranslation.pre_title, | ||||
|                     selectedTranslation.title, | ||||
|                     selectedTranslation.subtitle | ||||
|                   )} | ||||
|                   src={`${process.env.NEXT_PUBLIC_URL_ASSETS}/contents/videos/\ | ||||
| ${content.slug}_${selectedTranslation.language?.data?.attributes?.code}.mp4`}
 | ||||
|                   src={getVideoURL(content.slug, selectedTranslation.language.data.attributes.code)} | ||||
|                   subSrc={`${process.env.NEXT_PUBLIC_URL_ASSETS}/contents/videos/\ | ||||
| ${content.slug}_${selectedTranslation.language?.data?.attributes?.code}.vtt`}
 | ||||
| ${content.slug}_${selectedTranslation.language.data.attributes.code}.vtt`}
 | ||||
|                   className="max-w-[90vh]" | ||||
|                 /> | ||||
|               ) | ||||
| @ -340,7 +341,7 @@ export const getStaticProps: GetStaticProps = async (context) => { | ||||
|     return { notFound: true }; | ||||
|   } | ||||
| 
 | ||||
|   const { title, description } = (() => { | ||||
|   const { title, description, audio, video } = (() => { | ||||
|     if (context.locale && context.locales) { | ||||
|       const selectedTranslation = staticSmartLanguage({ | ||||
|         items: content.contents.data[0].attributes.translations, | ||||
| @ -351,6 +352,7 @@ export const getStaticProps: GetStaticProps = async (context) => { | ||||
|         const rawDescription = isDefinedAndNotEmpty(selectedTranslation.description) | ||||
|           ? selectedTranslation.description | ||||
|           : selectedTranslation.text_set?.text; | ||||
| 
 | ||||
|         return { | ||||
|           title: prettyInlineTitle( | ||||
|             selectedTranslation.pre_title, | ||||
| @ -366,12 +368,22 @@ export const getStaticProps: GetStaticProps = async (context) => { | ||||
|               ["attributes"] | ||||
|             ).map((category) => category.attributes.short), | ||||
|           }), | ||||
|           audio: | ||||
|             selectedTranslation.language?.data?.attributes?.code && selectedTranslation.audio_set | ||||
|               ? getAudioURL(slug, selectedTranslation.language.data.attributes.code) | ||||
|               : undefined, | ||||
|           video: | ||||
|             selectedTranslation.language?.data?.attributes?.code && selectedTranslation.video_set | ||||
|               ? getVideoURL(slug, selectedTranslation.language.data.attributes.code) | ||||
|               : undefined, | ||||
|         }; | ||||
|       } | ||||
|     } | ||||
|     return { | ||||
|       title: prettySlug(content.contents.data[0].attributes.slug), | ||||
|       description: undefined, | ||||
|       audio: undefined, | ||||
|       video: undefined, | ||||
|     }; | ||||
|   })(); | ||||
| 
 | ||||
| @ -379,7 +391,7 @@ export const getStaticProps: GetStaticProps = async (context) => { | ||||
| 
 | ||||
|   const props: Props = { | ||||
|     content: content.contents.data[0].attributes as ContentWithTranslations, | ||||
|     openGraph: getOpenGraph(format, title, description, thumbnail), | ||||
|     openGraph: getOpenGraph(format, title, description, thumbnail, audio, video), | ||||
|   }; | ||||
|   return { | ||||
|     props: props, | ||||
| @ -488,3 +500,16 @@ const RelatedContentPreview = ({ | ||||
|     /> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| /* | ||||
|  *                                      ╭───────────────────╮ | ||||
|  * ─────────────────────────────────────╯  PRIVATE METHODS  ╰─────────────────────────────────────── | ||||
|  */ | ||||
| 
 | ||||
| const getAudioURL = (slug: string, langCode: string): string => | ||||
|   `${process.env.NEXT_PUBLIC_URL_ASSETS}/contents/audios/\ | ||||
| ${slug}_${langCode}.mp3`;
 | ||||
| 
 | ||||
| const getVideoURL = (slug: string, langCode: string): string => | ||||
|   `${process.env.NEXT_PUBLIC_URL_ASSETS}/contents/videos/\ | ||||
| ${slug}_${langCode}.mp4`;
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 DrMint
						DrMint