From ef634194b8ac42414052f144710e4cc513e573e6 Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 15 Oct 2024 18:59:13 +0100 Subject: [PATCH] add cool og image for player embed --- .../backend/src/service/player.service.ts | 3 +- .../src/app/(pages)/api/og/player/route.tsx | 58 +++++++++++++++++++ .../src/app/(pages)/player/[...slug]/page.tsx | 18 +++--- projects/website/src/app/error.tsx | 27 ++++----- .../src/components/chart/generic-chart.tsx | 4 +- .../src/components/input/pagination.tsx | 2 +- 6 files changed, 83 insertions(+), 29 deletions(-) create mode 100644 projects/website/src/app/(pages)/api/og/player/route.tsx diff --git a/projects/backend/src/service/player.service.ts b/projects/backend/src/service/player.service.ts index 94abdc2..35537a0 100644 --- a/projects/backend/src/service/player.service.ts +++ b/projects/backend/src/service/player.service.ts @@ -123,6 +123,7 @@ export class PlayerService { */ public static async trackScore({ score, leaderboard }: ScoreSaberPlayerScoreToken) { const playerId = score.leaderboardPlayerInfo.id; + const playerName = score.leaderboardPlayerInfo.name; const player: PlayerDocument | null = await PlayerModel.findById(playerId); // Player is not tracked, so ignore the score. if (player == undefined) { @@ -151,7 +152,7 @@ export class PlayerService { await player.save(); console.log( - `Updated scores set statistic for "${player.id}", scores today: ${scores.rankedScores} ranked, ${scores.unrankedScores} unranked` + `Updated scores set statistic for "${playerName}"(${playerId}), scores today: ${scores.rankedScores} ranked, ${scores.unrankedScores} unranked` ); } } diff --git a/projects/website/src/app/(pages)/api/og/player/route.tsx b/projects/website/src/app/(pages)/api/og/player/route.tsx new file mode 100644 index 0000000..094dd31 --- /dev/null +++ b/projects/website/src/app/(pages)/api/og/player/route.tsx @@ -0,0 +1,58 @@ +import { ImageResponse } from "next/og"; +import { scoresaberService } from "@ssr/common/service/impl/scoresaber"; +import { NextRequest } from "next/server"; +import { formatNumberWithCommas, formatPp } from "@/common/number-utils"; +import { config } from "../../../../../../config"; + +export async function GET(request: NextRequest) { + const playerId = request.nextUrl.searchParams.get("id"); + if (!playerId) { + return new Response(null, { status: 400 }); + } + const player = await scoresaberService.lookupPlayer(playerId); + if (!player) { + return new Response(null, { status: 404 }); + } + + return new ImageResponse( + ( +
+ Player's Avatar +
+

{player.name}

+

{formatPp(player.pp)}pp

+
+
+ + + +

#{formatNumberWithCommas(player.rank)}

+
+
+ Player's Country +

#{formatNumberWithCommas(player.countryRank)}

+
+
+
+
+ ), + { + width: 1200, + height: 630, + emoji: "twemoji", + } + ); +} diff --git a/projects/website/src/app/(pages)/player/[...slug]/page.tsx b/projects/website/src/app/(pages)/player/[...slug]/page.tsx index c651d13..155635c 100644 --- a/projects/website/src/app/(pages)/player/[...slug]/page.tsx +++ b/projects/website/src/app/(pages)/player/[...slug]/page.tsx @@ -1,6 +1,4 @@ -import { formatNumberWithCommas, formatPp } from "@/common/number-utils"; import PlayerData from "@/components/player/player-data"; -import { format } from "@formkit/tempo"; import { Metadata, Viewport } from "next"; import { redirect } from "next/navigation"; import { Colors } from "@/common/colors"; @@ -97,20 +95,20 @@ export async function generateMetadata(props: Props): Promise { title: `${player.name}`, openGraph: { title: `ScoreSaber Reloaded - ${player.name}`, - description: ` - PP: ${formatPp(player.pp)}pp - Rank: #${formatNumberWithCommas(player.rank)} (#${formatNumberWithCommas(player.countryRank)} ${player.country}) - Joined ScoreSaber: ${format(player.joinedDate, { date: "medium", time: "short" })} - - View the scores for ${player.name}!`, + // description: ` + // PP: ${formatPp(player.pp)}pp + // Rank: #${formatNumberWithCommas(player.rank)} (#${formatNumberWithCommas(player.countryRank)} ${player.country}) + // Joined ScoreSaber: ${format(player.joinedDate, { date: "medium", time: "short" })} + // + // View the scores for ${player.name}!`, images: [ { - url: player.avatar, + url: `${config.siteUrl}/api/og/player/?id=${player.id}`, }, ], }, twitter: { - card: "summary", + card: "summary_large_image", }, }; } diff --git a/projects/website/src/app/error.tsx b/projects/website/src/app/error.tsx index 5799d5a..132afef 100644 --- a/projects/website/src/app/error.tsx +++ b/projects/website/src/app/error.tsx @@ -2,25 +2,22 @@ import { GlobeAmericasIcon } from "@heroicons/react/24/solid"; import Link from "next/link"; -import Card from "@/components/card"; export default function Error({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) { return ( - -
- -

Oops! Something went wrong.

-

- We're experiencing some technical difficulties. Please try again later. -

- {error?.digest &&

Error Code: {error.digest}

} +
+ +

Oops! Something went wrong.

+

+ We're experiencing some technical difficulties. Please try again later. +

+ {error?.digest &&

Error Code: {error.digest}

} -
- - Go back to the homepage - -
+
+ + Go back to the homepage +
- +
); } diff --git a/projects/website/src/components/chart/generic-chart.tsx b/projects/website/src/components/chart/generic-chart.tsx index 539f9ba..2ec0816 100644 --- a/projects/website/src/components/chart/generic-chart.tsx +++ b/projects/website/src/components/chart/generic-chart.tsx @@ -2,12 +2,12 @@ "use client"; import { Chart, registerables } from "chart.js"; -Chart.register(...registerables); - import { Line } from "react-chartjs-2"; import { useIsMobile } from "@/hooks/use-is-mobile"; import { formatDateMinimal, getDaysAgo, getDaysAgoDate, parseDate } from "@ssr/common/utils/time-utils"; +Chart.register(...registerables); + export type AxisPosition = "left" | "right"; export type DatasetDisplayType = "line" | "bar"; diff --git a/projects/website/src/components/input/pagination.tsx b/projects/website/src/components/input/pagination.tsx index ca45cdf..530465b 100644 --- a/projects/website/src/components/input/pagination.tsx +++ b/projects/website/src/components/input/pagination.tsx @@ -1,5 +1,6 @@ import { ArrowPathIcon } from "@heroicons/react/24/solid"; import clsx from "clsx"; +import * as React from "react"; import { useEffect, useState } from "react"; import { Pagination as ShadCnPagination, @@ -10,7 +11,6 @@ import { PaginationNext, PaginationPrevious, } from "../ui/pagination"; -import * as React from "react"; import { ChevronDoubleLeftIcon, ChevronDoubleRightIcon } from "@heroicons/react/16/solid"; type PaginationItemWrapperProps = {