20 try {
21 setLoading(true);
22 const response = await fetch(`/api/voicenotes/${voiceNoteId}`);
23 const data = await response.json();
24
26 setVoiceNote(data.voiceNote);
27 // Get audio URL
28 setAudioUrl(`/api/voicenotes/${voiceNoteId}/audio`);
29 } else {
30 setError(data.error || 'Voice note not found');
50## Tech Stack
51
52- **Backend**: Hono (TypeScript API framework)
53- **Database**: SQLite for voice note metadata
54- **Storage**: Val Town Blob storage for audio files
20
21// Mount routes
22app.route("/api/voicenotes", voiceNotesRoutes);
23app.route("/", staticRoutes);
24
15 try {
16 setLoading(true);
17 const response = await fetch('/api/voicenotes');
18 const data = await response.json();
19
120
121 try {
122 const response = await fetch(`/api/voicenotes/${voiceNoteId}`, {
123 method: 'DELETE'
124 });
2import React from "https://esm.sh/react";
3import { renderToString } from "https://esm.sh/react-dom/server";
4import GetWeather, { type WEATHER_FORECAST } from "../api/weather.ts";
5import { BodyWrapper, Content, Footer, Header, Headline } from "../components.tsx";
6
34 const url = new URL(req.url);
35
36 const apiUrl =
37 `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t_weather=true&temperature_unit=fahrenheit`;
38 const cacheKey = `weather-forecast-${latitude}-${longitude}`;
39
40 // Cache for 30 minutes
41 const data: WEATHER_FORECAST = await fetchWithCache(apiUrl, cacheKey, 30).then((res) => res.json());
42 return new Response(JSON.stringify(data), {
43 headers: {
23## How to use this Val
24
25🔑 **Important** https://apiflash.com free key required for generating screenshots. (Will update with option to generate on device)
26
27🔑 **GitHub Frame** Requires a GitHub personal access token set as `GITHUB_API_KEY` environment variable for the GitHub activity frame.
28This only needs read access to `contents` (commits, branches, downloads, releases, and merges).
29
30This Val is separated into 2 directories, the `api/` and the `frames/`.
31Take a look at each frame to see an example of what will be displayed on your e-ink display.
32
33The `api/` directory holds endpoints for each Val. These endpoints could be used across projects
34and will soon be moved to their own. Each one is cached server side (on val.town) to reduce traffic to free/public apis.
35
36Remixes and pull requests welcome!
2import React from "https://esm.sh/react";
3import { renderToString } from "https://esm.sh/react-dom/server/";
4import GetNews from "../api/news.ts";
5import { BodyWrapper, Content, Footer, Header, Headline } from "../components.tsx";
6
7// REQUIRES API KEY - see api/news.ts
8function Render({ news }: { news: Record<string, any> }) {
9 const line = "To be taught if fortunate";
1import fetchWithCache from "./fetchWithCache.ts";
2
3const GUARDIAN_API_KEY = Deno.env.get("GUARDIAN_API_KEY");
4
5export default async function(req: Request): Promise<Response> {
21 };
22
23 const address = `https://content.guardianapis.com/search?api-key=${GUARDIAN_API_KEY}&page-size=${size}`;
24 const apiUrl = address + `&${new URLSearchParams(searchParameters).toString()}`;
25 const cacheKey = `guardian_headlines_${size}`;
26
27 const data = await fetchWithCache(apiUrl, cacheKey).then((res) => res.json());
28 return new Response(JSON.stringify(data), {
29 headers: {
2import React from "https://esm.sh/react";
3import { renderToString } from "https://esm.sh/react-dom/server";
4import GetLogs, { type HEMOLOG_TREATMENT } from "../api/hemolog.ts";
5import { BodyWrapper, Content, Footer, Header, Headline } from "../components.tsx";
6