Skip to content

Commit

Permalink
chore: initial tests for Stamp Detail page - sharing #488
Browse files Browse the repository at this point in the history
  • Loading branch information
reinamora137 committed Dec 18, 2024
1 parent 3ec0bb1 commit 0139c3e
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 12 deletions.
1 change: 1 addition & 0 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"tls": "node:tls",
"net": "node:net",
"dns": "node:dns",
"puppeteer": "npm:puppeteer@^21.5.0",
"dns/promises": "node:dns/promises",
"@preact/signals": "https://esm.sh/*@preact/signals@1.2.2",
"@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.5.1",
Expand Down
2 changes: 2 additions & 0 deletions fresh.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import * as $api_v2_src20_tick_tick_index from "./routes/api/v2/src20/tick/[tick
import * as $api_v2_src20_tick_tick_mintData from "./routes/api/v2/src20/tick/[tick]/mintData.ts";
import * as $api_v2_src20_tick_index from "./routes/api/v2/src20/tick/index.ts";
import * as $api_v2_src20_tx_tx_hash_ from "./routes/api/v2/src20/tx/[tx_hash].ts";
import * as $api_v2_stamp_stamp_preview from "./routes/api/v2/stamp/[stamp]/preview.ts";
import * as $api_v2_stamps_id_ from "./routes/api/v2/stamps/[id].ts";
import * as $api_v2_stamps_id_dispensers from "./routes/api/v2/stamps/[id]/dispensers.ts";
import * as $api_v2_stamps_id_dispenses from "./routes/api/v2/stamps/[id]/dispenses.ts";
Expand Down Expand Up @@ -305,6 +306,7 @@ const manifest = {
$api_v2_src20_tick_tick_mintData,
"./routes/api/v2/src20/tick/index.ts": $api_v2_src20_tick_index,
"./routes/api/v2/src20/tx/[tx_hash].ts": $api_v2_src20_tx_tx_hash_,
"./routes/api/v2/stamp/[stamp]/preview.ts": $api_v2_stamp_stamp_preview,
"./routes/api/v2/stamps/[id].ts": $api_v2_stamps_id_,
"./routes/api/v2/stamps/[id]/dispensers.ts": $api_v2_stamps_id_dispensers,
"./routes/api/v2/stamps/[id]/dispenses.ts": $api_v2_stamps_id_dispenses,
Expand Down
92 changes: 92 additions & 0 deletions routes/api/v2/stamp/[stamp]/preview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Handlers } from "$fresh/server.ts";
import { StampController } from "$server/controller/stampController.ts";
import puppeteer from "npm:puppeteer";

export const handler: Handlers = {
async GET(req, ctx) {
try {
const { stamp } = ctx.params;

// Get stamp details
const stampData = await StampController.getStampDetailsById(stamp);
if (!stampData?.data?.stamp) {
return new Response("Stamp not found", { status: 404 });
}

const { stamp_url, stamp_mimetype } = stampData.data.stamp;

// If it's already an image (not HTML/SVG), redirect to original
if (
stamp_mimetype?.startsWith("image/") &&
stamp_mimetype !== "image/svg+xml"
) {
return new Response(null, {
status: 302,
headers: { Location: stamp_url },
});
}

// Launch headless browser
const browser = await puppeteer.launch({
headless: "new",
args: ["--no-sandbox", "--disable-setuid-sandbox"],
});

try {
const page = await browser.newPage();

// Set viewport size to square dimensions
// Using 1200x1200 for high quality, can be adjusted if needed
await page.setViewport({
width: 1200,
height: 1200,
deviceScaleFactor: 1,
});

// Load the stamp content
await page.goto(stamp_url, {
waitUntil: "networkidle0",
timeout: 10000,
});

// For HTML content, wait for any animations/renders
if (stamp_mimetype === "text/html") {
await new Promise((resolve) => setTimeout(resolve, 1000));
}

// Take screenshot
const screenshot = await page.screenshot({
type: "webp",
quality: 90,
encoding: "binary",
fullPage: false,
clip: {
x: 0,
y: 0,
width: 1200,
height: 1200,
},
});

return new Response(screenshot, {
headers: {
"Content-Type": "image/webp",
"Cache-Control": "public, max-age=31536000",
},
});
} finally {
await browser.close();
}
} catch (error) {
console.error("Preview generation error:", error);

// Return a default preview image on error
return new Response(null, {
status: 302,
headers: {
Location: "/static/images/default-preview.png",
},
});
}
},
};
56 changes: 45 additions & 11 deletions routes/stamp/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,6 @@ interface StampData {
lowestPriceDispenser: any;
}

// Update CollectionRow interface to be simpler since we only need basic info
interface CollectionRow {
collection_id: string;
name: string;
}

export const handler: Handlers<StampData> = {
async GET(req: Request, ctx) {
try {
Expand Down Expand Up @@ -153,6 +147,7 @@ export const handler: Handlers<StampData> = {
stamps_recent: recentStamps?.data || [],
holders: calculateHoldersWithPercentage(holders.data),
lowestPriceDispenser: lowestPriceDispenser,
url: req.url,
});
} catch (error) {
console.error("Error fetching stamp data:", error);
Expand Down Expand Up @@ -181,6 +176,32 @@ export default function StampPage(props: StampDetailPageProps) {
? `${stamp.name}`
: `Bitcoin Stamp #${stamp.stamp} - stampchain.io`;

// Update the getMetaImageUrl and add dimension handling
const getMetaImageInfo = (stamp: StampRow, baseUrl: string) => {
// For HTML/SVG content, use preview endpoint with known dimensions
if (
stamp.stamp_mimetype === "text/html" ||
stamp.stamp_mimetype === "image/svg+xml"
) {
return {
url: `${baseUrl}/api/v2/stamp/${stamp.stamp}/preview`,
width: 1200,
height: 1200,
};
}

// For direct images, use original URL and dimensions
return {
url: stamp.stamp_url,
};
};

const baseUrl = new URL(props.url).origin;
const metaInfo = getMetaImageInfo(stamp, baseUrl);
const metaDescription = `Bitcoin Stamp #${stamp.stamp} - ${
stamp.name || "Unprunable UTXO Art"
}`;

const bodyClassName = "flex flex-col gap-6";

const latestStampsSection = {
Expand Down Expand Up @@ -217,13 +238,26 @@ export default function StampPage(props: StampDetailPageProps) {
<Head>
<title>{title}</title>
<meta property="og:title" content={title} />
<meta
property="og:description"
content="Unprunable UTXO Art, Because Sats Don't Exist"
/>
<meta property="og:image" content={stamp.stamp_url} />
<meta property="og:description" content={metaDescription} />
<meta property="og:image" content={metaInfo.url} />
<meta property="og:type" content="website" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={metaDescription} />
<meta name="twitter:image" content={metaInfo.url} />
{/* Only add dimension meta tags if we have the dimensions */}
{metaInfo.width && metaInfo.height && (
<>
<meta
property="og:image:width"
content={metaInfo.width.toString()}
/>
<meta
property="og:image:height"
content={metaInfo.height.toString()}
/>
</>
)}
</Head>

<div class={bodyClassName}>
Expand Down
2 changes: 1 addition & 1 deletion server/controller/stampController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ export class StampController {
stampCategories,
] = await Promise.all([
this.getMultipleStampCategories([
{ idents: ["SRC-721"], limit: 12 },
{ idents: ["SRC-721"], limit: 12, sortBy: "DESC" },
]),
]);
// Fetch the "posh" collection to get its collection_id
Expand Down

0 comments on commit 0139c3e

Please sign in to comment.