|
| 1 | +import { Hono } from "hono"; |
| 2 | +import { registerSnapHandler } from "@farcaster/snap-hono"; |
| 3 | +import type { SnapHandlerResult } from "@farcaster/snap"; |
| 4 | + |
| 5 | +const app = new Hono(); |
| 6 | + |
| 7 | +registerSnapHandler(app, async (ctx): Promise<SnapHandlerResult> => { |
| 8 | + const base = snapBaseUrlFromRequest(ctx.request); |
| 9 | + const pressed = |
| 10 | + ctx.action.type === "post" && typeof ctx.action.inputs.mine === "string" |
| 11 | + ? ctx.action.inputs.mine |
| 12 | + : null; |
| 13 | + |
| 14 | + return boardPage(base, pressed); |
| 15 | +}); |
| 16 | + |
| 17 | +export default app; |
| 18 | + |
| 19 | +function boardPage(base: string, pressed: string | null): SnapHandlerResult { |
| 20 | + return { |
| 21 | + version: "2.0", |
| 22 | + theme: { accent: "green" }, |
| 23 | + ui: { |
| 24 | + root: "page", |
| 25 | + elements: { |
| 26 | + page: { |
| 27 | + type: "stack", |
| 28 | + props: { gap: "sm" }, |
| 29 | + children: ["title", "hint", "board", "pressed"], |
| 30 | + }, |
| 31 | + title: { |
| 32 | + type: "item", |
| 33 | + props: { |
| 34 | + title: "Square cell grid", |
| 35 | + description: "A 9x9 board using cellAspectRatio: square.", |
| 36 | + media: { variant: "icon", name: "grid-3x3", color: "green" }, |
| 37 | + }, |
| 38 | + children: ["badge"], |
| 39 | + }, |
| 40 | + badge: { |
| 41 | + type: "badge", |
| 42 | + props: { label: "2.0", color: "green" }, |
| 43 | + }, |
| 44 | + hint: { |
| 45 | + type: "text", |
| 46 | + props: { |
| 47 | + content: "Cells should remain square as the emulator width changes.", |
| 48 | + size: "sm", |
| 49 | + color: "muted", |
| 50 | + }, |
| 51 | + }, |
| 52 | + board: { |
| 53 | + type: "cell_grid", |
| 54 | + props: { |
| 55 | + name: "mine", |
| 56 | + cols: 9, |
| 57 | + rows: 9, |
| 58 | + gap: "sm", |
| 59 | + cellAspectRatio: "square", |
| 60 | + cells: mineCells(), |
| 61 | + }, |
| 62 | + on: { |
| 63 | + press: { |
| 64 | + action: "submit", |
| 65 | + params: { target: `${base}/` }, |
| 66 | + }, |
| 67 | + }, |
| 68 | + }, |
| 69 | + pressed: { |
| 70 | + type: "text", |
| 71 | + props: { |
| 72 | + content: pressed ? `Last press: ${pressed}` : "Press a cell to submit coordinates.", |
| 73 | + size: "sm", |
| 74 | + align: "center", |
| 75 | + }, |
| 76 | + }, |
| 77 | + }, |
| 78 | + }, |
| 79 | + }; |
| 80 | +} |
| 81 | + |
| 82 | +function mineCells(): Array<{ |
| 83 | + row: number; |
| 84 | + col: number; |
| 85 | + color?: string; |
| 86 | + content?: string; |
| 87 | +}> { |
| 88 | + const mines = new Set(["0,1", "1,3", "2,7", "4,4", "6,2", "7,6", "8,0"]); |
| 89 | + const numbers = new Map<string, string>([ |
| 90 | + ["0,0", "1"], |
| 91 | + ["0,2", "1"], |
| 92 | + ["1,1", "1"], |
| 93 | + ["1,2", "1"], |
| 94 | + ["2,3", "1"], |
| 95 | + ["3,4", "1"], |
| 96 | + ["4,3", "1"], |
| 97 | + ["4,5", "1"], |
| 98 | + ["5,2", "1"], |
| 99 | + ["6,6", "1"], |
| 100 | + ["7,0", "1"], |
| 101 | + ]); |
| 102 | + |
| 103 | + const cells = []; |
| 104 | + for (let row = 0; row < 9; row++) { |
| 105 | + for (let col = 0; col < 9; col++) { |
| 106 | + const key = `${row},${col}`; |
| 107 | + const isMine = mines.has(key); |
| 108 | + const content = isMine ? "*" : numbers.get(key); |
| 109 | + cells.push({ |
| 110 | + row, |
| 111 | + col, |
| 112 | + color: isMine ? "#ef4444" : (row + col) % 2 === 0 ? "#d1fae5" : "#a7f3d0", |
| 113 | + ...(content ? { content } : {}), |
| 114 | + }); |
| 115 | + } |
| 116 | + } |
| 117 | + return cells; |
| 118 | +} |
| 119 | + |
| 120 | +function snapBaseUrlFromRequest(request: Request): string { |
| 121 | + const fromEnv = process.env.SNAP_PUBLIC_BASE_URL?.trim(); |
| 122 | + if (fromEnv) return fromEnv.replace(/\/$/, ""); |
| 123 | + |
| 124 | + const proto = request.headers.get("x-forwarded-proto")?.trim() || "https"; |
| 125 | + const host = |
| 126 | + request.headers.get("x-forwarded-host")?.trim() || |
| 127 | + request.headers.get("host")?.trim(); |
| 128 | + if (host) return `${proto}://${host}`.replace(/\/$/, ""); |
| 129 | + |
| 130 | + return `http://localhost:${process.env.PORT ?? "3022"}`.replace(/\/$/, ""); |
| 131 | +} |
0 commit comments