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