1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/** @jsxImportSource https://esm.sh/react */
import React from "https://esm.sh/react";
import { renderToString } from "https://esm.sh/react-dom/server";
import { DateTime } from "https://esm.sh/luxon";
const BROOKLYN_HOURLY_FORECAST = "https://api.weather.gov/gridpoints/OKX/35,34/forecast/hourly";
async function getHourlyWeather(day: DateTime): Promise<any[]> {
const response = await fetch(BROOKLYN_HOURLY_FORECAST);
const { properties: { periods } } = await response.json();
return periods.filter((x) =>
DateTime.fromISO(x.startTime) >= day.startOf('day') &&
DateTime.fromISO(x.startTime) < day.endOf('day')
);
}
function WeatherChart({ periods }) {
const width = 300;
const height = 150;
const maxTemp = Math.max(...periods.map(p => p.temperature));
const minTemp = Math.min(...periods.map(p => p.temperature));
return (
<svg viewBox={`0 0 ${width} ${height}`} width="100%">
{periods.map((period, i) => {
const x = (i / (periods.length - 1)) * width;
const y = height - ((period.temperature - minTemp) / (maxTemp - minTemp)) * height;
return (
<React.Fragment key={i}>
<circle cx={x} cy={y} r="3" fill="orange" />
{i % 4 === 0 && (
<text x={x} y={height - 5} textAnchor="middle" fontSize="10">
{period.startTime.slice(11, 16)}
</text>
)}
</React.Fragment>
);
})}
</svg>
);
}
async function server(req: Request): Promise<Response> {
const now = DateTime.now().setZone("America/New_York");
const periods = await getHourlyWeather(now);
return new Response(
renderToString(
<html>
<head>
<title>Brooklyn Weather</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>{`
body { font-family: sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
`}</style>
</head>
<body>
<h1>Brooklyn Weather Forecast</h1>
<WeatherChart periods={periods} />
<p>Data from <a href="https://www.weather.gov/">National Weather Service</a></p>
<p><a href={import.meta.url.replace("esm.town", "val.town")}>View source</a></p>
</body>
</html>
),
{
headers: { "Content-Type": "text/html" },
}
);
}
export default server;