-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(social): Add generic OpenGraph image
- Loading branch information
1 parent
8c18c45
commit d533d62
Showing
3 changed files
with
189 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { loadGoogleFont } from '@/core/utils/social'; | ||
import { viewsService } from '@/services/views.service'; | ||
import { ImageResponse } from 'next/og'; | ||
|
||
export const runtime = 'edge'; | ||
|
||
export const alt = 'Kanojo'; | ||
|
||
export const contentType = 'image/png'; | ||
|
||
/** | ||
* Generates an Open Graph image response for the Kanojo application. | ||
* | ||
* This function creates an image response with a styled div containing | ||
* a title and a description. The image is intended to be used as an | ||
* Open Graph image for social media sharing. | ||
* @returns An ImageResponse object. | ||
*/ | ||
export default async function Image() { | ||
const counts = await viewsService.getCurrentCounts(); | ||
|
||
return new ImageResponse( | ||
( | ||
<div tw="flex flex-row w-full h-full items-center justify-center bg-pink-200"> | ||
<div tw="flex flex-col items-center justify-center mr-30"> | ||
<h1 tw="self-center whitespace-nowrap border-l-8 border-l-pink-500 pl-3 text-9xl font-extrabold"> | ||
Kanojo | ||
</h1> | ||
<p tw="text-4xl font-bold text-center mb-0"> | ||
A community-run database | ||
</p> | ||
<p tw="text-4xl font-bold text-center mt-0">for gravure idols.</p> | ||
</div> | ||
<div tw="flex flex-col border-l-4 border-pink-500 pl-24 mr-8"> | ||
<div tw="flex flex-col"> | ||
<h2 tw="text-2xl font-bold mb-0">Movies</h2> | ||
<p tw="text-6xl font-black text-pink-600 mt-2"> | ||
{counts?.movie_count} | ||
</p> | ||
</div> | ||
<div tw="flex flex-col"> | ||
<h2 tw="text-2xl font-bold mb-0">People</h2> | ||
<p tw="text-6xl font-black text-pink-600 mt-2"> | ||
{counts?.person_count} | ||
</p> | ||
</div> | ||
<div tw="flex flex-col"> | ||
<h2 tw="text-2xl font-bold mb-0">Studios</h2> | ||
<p tw="text-6xl font-black text-pink-600 mt-2"> | ||
{counts?.studio_count} | ||
</p> | ||
</div> | ||
</div> | ||
</div> | ||
), | ||
{ | ||
fonts: [ | ||
{ | ||
data: await loadGoogleFont('Noto+Sans+JP', '700'), | ||
name: 'Noto Sans JP', | ||
style: 'normal', | ||
}, | ||
{ | ||
data: await loadGoogleFont('Noto+Sans+JP', '800'), | ||
name: 'Noto Sans JP', | ||
style: 'normal', | ||
}, | ||
{ | ||
data: await loadGoogleFont('Noto+Sans+JP', '900'), | ||
name: 'Noto Sans JP', | ||
style: 'normal', | ||
}, | ||
], | ||
height: 630, | ||
width: 1200, | ||
}, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/** | ||
* Asynchronously loads a Google Font and returns its data as an ArrayBuffer. | ||
* @param font - The name of the Google Font to load. | ||
* @param weight - The weight of the font to load. | ||
* @returns A promise that resolves to an ArrayBuffer containing the font data. | ||
* @throws Will throw an error if the font data could not be loaded. | ||
*/ | ||
export async function loadGoogleFont( | ||
font: string, | ||
weight: | ||
| "100" | ||
| "200" | ||
| "300" | ||
| "400" | ||
| "500" | ||
| "600" | ||
| "700" | ||
| "800" | ||
| "900" = "400", | ||
): Promise<ArrayBuffer> { | ||
const url = `https://fonts.googleapis.com/css2?family=${font}:wght@${weight}`; | ||
const cssFile = await fetch(url); | ||
const css = await cssFile.text(); | ||
const resource = /src: url\((.+)\) format\('(opentype|truetype)'\)/.exec(css); | ||
|
||
if (resource) { | ||
const response = await fetch(resource[1]); | ||
if (response.status == 200) { | ||
return await response.arrayBuffer(); | ||
} | ||
} | ||
|
||
throw new Error("failed to load font data"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { describe, expect, it } from "vitest"; | ||
|
||
import { | ||
apiMovieSearchResultSchema, | ||
apiMovieSearchResultSchemaArray, | ||
} from "./api"; | ||
|
||
describe("API schema - movie search result", () => { | ||
it("should validate a correct movie search result", () => { | ||
const validData = { | ||
id: 1, | ||
original_title: "Inception", | ||
release_date: "2010-07-16", | ||
title: "Inception", | ||
}; | ||
|
||
expect(() => apiMovieSearchResultSchema.parse(validData)).not.toThrow(); | ||
}); | ||
|
||
it("should invalidate an incorrect movie search result", () => { | ||
const invalidData = { | ||
id: "1", // id should be a number | ||
original_title: "Inception", | ||
}; | ||
|
||
expect(() => apiMovieSearchResultSchema.parse(invalidData)).toThrow(); | ||
}); | ||
|
||
it("should validate a movie search result with optional fields", () => { | ||
const validData = { | ||
id: 1, | ||
original_title: "Inception", | ||
}; | ||
|
||
expect(() => apiMovieSearchResultSchema.parse(validData)).not.toThrow(); | ||
}); | ||
}); | ||
|
||
describe("API schema - movie search result array", () => { | ||
it("should validate an array of correct movie search results", () => { | ||
const validDataArray = [ | ||
{ | ||
id: 1, | ||
original_title: "Inception", | ||
release_date: "2010-07-16", | ||
title: "Inception", | ||
}, | ||
{ | ||
id: 2, | ||
original_title: "The Matrix", | ||
release_date: "1999-03-31", | ||
title: "The Matrix", | ||
}, | ||
]; | ||
|
||
expect(() => apiMovieSearchResultSchemaArray.parse(validDataArray)).not | ||
.toThrow(); | ||
}); | ||
|
||
it("should invalidate an array with an incorrect movie search result", () => { | ||
const invalidDataArray = [ | ||
{ | ||
id: 1, | ||
original_title: "Inception", | ||
release_date: "2010-07-16", | ||
title: "Inception", | ||
}, | ||
{ | ||
id: "2", // id should be a number | ||
original_title: "The Matrix", | ||
}, | ||
]; | ||
|
||
expect(() => apiMovieSearchResultSchemaArray.parse(invalidDataArray)) | ||
.toThrow(); | ||
}); | ||
}); |