display cart images

This commit is contained in:
Dylan Pizzo
2026-06-14 22:23:37 -04:00
parent 19e0ae4605
commit f3d84f3c55
4 changed files with 90 additions and 12 deletions
+16 -3
View File
@@ -1,11 +1,12 @@
import { Link, useParams } from "react-router-dom"; import { Link, useParams } from "react-router-dom";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { css } from "@emotion/css"; import { css } from "@emotion/css";
import { Game } from "../server/util/types";
// import { type Pico8Player } from "@athingperday/react-pico-player"; // import { type Pico8Player } from "@athingperday/react-pico-player";
type Info = { type Info = {
author: string | null; author: string | null;
games: string[]; games: Game[];
}; };
export const AuthorPage = () => { export const AuthorPage = () => {
@@ -44,11 +45,23 @@ export const AuthorPage = () => {
`} `}
> >
<h1>{author}</h1> <h1>{author}</h1>
<div
className={css`
display: flex;
flex-wrap: wrap;
`}
>
{info.games.map((game) => ( {info.games.map((game) => (
<Link key={game} to={`/u/${author}/${game}`}> <Link
<h3>{game}</h3> key={game.carts[0].name}
to={`/u/${author}/${game.carts[0].name}`}
>
<h3>
<img src={game.carts[0].src} />
</h3>
</Link> </Link>
))} ))}
</div> </div>
</div>
); );
}; };
+14 -6
View File
@@ -3,6 +3,8 @@ import { FirRouteInput, FirRouteOptions } from "../util/routewrap.ts";
import { authors } from "../../data/authors.ts"; import { authors } from "../../data/authors.ts";
import { Octokit } from "octokit"; import { Octokit } from "octokit";
import { decrypt } from "../util/crypt.ts"; import { decrypt } from "../util/crypt.ts";
import { Game } from "../util/types.ts";
import { getGame, getGameFromSha } from "../util/getData.ts";
const method = "GET"; const method = "GET";
const url = "/api/author"; const url = "/api/author";
@@ -38,15 +40,21 @@ const handler = async ({ payload }: FirRouteInput<typeof payloadT>) => {
}); });
// TODO: get the games. // TODO: get the games.
const games: string[] = []; const games: Game[] = [];
if (Array.isArray(gameStuff.data)) { if (Array.isArray(gameStuff.data)) {
games.push( const newGames = await Promise.all(
...gameStuff.data gameStuff.data
.map((x) => x.name) .filter((x) => x.name.endsWith(".p8.png") && x.type === "file")
.filter((x) => x.endsWith(".p8.png")) .flatMap(async (x) => {
.map((x) => x.slice(0, -".p8.png".length)), const name = x.name.slice(0, -".p8.png".length);
const game =
(await getGameFromSha(x.sha)) ??
(await getGame(authorData.username, name));
return game ? [game] : [];
}),
); );
games.push(...newGames.flat());
} }
return { return {
+56
View File
@@ -0,0 +1,56 @@
import { Octokit } from "octokit";
import { decrypt } from "./crypt";
import { authors } from "../../data/authors";
type Game = { carts: { name: string; src: string }[] };
const gameCache: Record<string, Game> = {};
export const getGame = async (
author: string,
name: string,
): Promise<Game | null> => {
const authorData = authors.find((x) => x.username === author);
if (!authorData) {
return null;
}
const pat = decrypt({
password: process.env.ENCRYPTION_PASSWORD!,
cyphertext: authorData.auth.pat,
});
const octokit = new Octokit({
auth: pat,
});
const gameData = (
await octokit.rest.repos.getContent({
owner: authorData.username,
repo: authorData.repo,
path: `.picobook/${name}.p8.png`,
})
).data;
if (Array.isArray(gameData)) {
return null;
}
if (gameData.type !== "file") {
return null;
}
if (!(gameData.sha in gameCache)) {
gameCache[gameData.sha] = {
carts: [{ name, src: `data:image/png;base64,${gameData.content}` }],
};
}
return gameCache[gameData.sha];
};
export const getGameFromSha = async (sha: string): Promise<Game | null> => {
if (gameCache[sha]) {
console.log("cache hit");
}
return gameCache[sha] ?? null;
};
+1
View File
@@ -0,0 +1 @@
export type Game = { carts: { name: string; src: string }[] };