1# Live reload in new tabs
2
3When you're working on an HTML HTTP val in a new tab, it's annoying to have to manually reload the tab on every save. In the Val Town editor, you can hit cmd+enter, but there's nothing like that for a val in a new tab because Val Town doesn't control that new tab (like we control the iframe in the browser preview). However, you control that HTML via the fetch handler you're writing, so you can add a script that polls the Val Town API for the current version number of your val, and reload itself if it detects a new version. This val has a collection of helpers to help you do just that.
4
5## Usage
7```ts
8import { html } from "https://esm.town/v/stevekrouse/html";
9import { reloadOnSaveFetchMiddleware } from "https://esm.town/v/stevekrouse/reloadOnSave";
10
11export default reloadOnSaveFetchMiddleware(async function(req: Request): Promise<Response> {
12 return html(`<h1>Hello!!</h1>`);
13})
37
38/**
39 * @param handler http val's fetch handler
40 * @param vals to watch
41 */
42export function reloadOnSaveFetchMiddleware(
43 handler: (req: Request) => Response | Promise<Response>,
44 vals = [rootValRef()],
24
25 useEffect(() => {
26 fetchVotes();
27 }, []);
28
29 const fetchVotes = async () => {
30 try {
31 const response = await fetch("/votes");
32 const data = await response.json();
33 console.log("Fetched votes:", data);
34 setVotes(data);
35 } catch (error) {
36 console.error("Error fetching votes:", error);
37 }
38 };
41 console.log("Voting for location:", locationId);
42 try {
43 const response = await fetch("/vote", {
44 method: "POST",
45 headers: { "Content-Type": "application/json" },
156 `);
157 const votes = Object.fromEntries(result.rows.map(row => [row.location_id, row.count]));
158 console.log("Fetched votes:", votes);
159 return new Response(JSON.stringify(votes), {
160 headers: { "Content-Type": "application/json" },
161 });
162 } catch (error) {
163 console.error("Error fetching votes:", error);
164 return new Response(JSON.stringify({ error: error.message }), {
165 status: 500,
24
25 useEffect(() => {
26 fetchScrapsAndGenerateSummary(currentWeek);
27 updateUrlState(currentWeek);
28 }, [currentWeek]);
29
30 const fetchScrapsAndGenerateSummary = async (weekStart: Date) => {
31 setLoading(true);
32 setSummary("");
35 try {
36 setDebugInfo(
37 prev => [...prev, `Fetching scraps and generating summary for week of ${weekStart.toDateString()}...`],
38 );
39 const response = await fetch(`/api/summary?week=${weekStart.toISOString()}`);
40 const data = await response.json();
41
46 setDebugInfo(prev => [
47 ...prev,
48 `Fetched ${data.scrapCount} scraps for the week of ${weekStart.toDateString()}`,
49 "Summary generated successfully",
50 ]);
52 setMetadata(data.metadata);
53 } catch (error) {
54 console.error("Error fetching summary:", error);
55 setDebugInfo(prev => [...prev, `Error: ${error.message}`]);
56 setSummary("Error generating summary. Please check the debug information and try again later.");
177
178 if (error) {
179 return new Response(JSON.stringify({ error: "Error fetching scraps: " + error.message }), {
180 status: 500,
181 headers: { "Content-Type": "application/json" },
9export default async function notify() {
10 try {
11 // Fetch and parse the RSS feed
12 const rss = await Parse.parse("https://medium.com/feed/@admiralcloudberg");
13
918 code: newCode,
919 });
920 fetch('/save', { method: "POST", body }).then(() => {
921 document.getElementById('code-input-hidden').value = newCode;
922 document.getElementById('preview-iframe').src += '';
1284 app.post("/", mainHandler);
1285
1286 return passwordAuth(app.fetch, { verifyPassword: verifyToken });
1287}
53
54const getNews = async ({ query }: { query: string }) => {
55 const response = await fetch(
56 getFeedUrl(query),
57 );
1import { fetchJSON } from "https://esm.town/v/stevekrouse/fetchJSON?v=41";
2
3// GitHub gists
4export let githubGists = fetchJSON(
5 "https://api.github.com/users/stevekrouse/gists",
6);
119 function updateExistingCodes() {
120 const category = document.getElementById('filterCategory').value;
121 fetch('/existing-qr-codes?category=' + encodeURIComponent(category))
122 .then(response => response.json())
123 .then(codes => {
205 }
206
207 fetch('/generate', {
208 method: 'POST',
209 headers: {
1import { fetchJSON } from "https://esm.town/v/stevekrouse/fetchJSON";
2
3export const nasaImageDetails = async () => {
4 const nasaAPOD = await fetchJSON("https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY");
5 let nasaImageHtml = nasaAPOD.hdurl
6 ? `<img width="100%" src="${nasaAPOD.hdurl}"/>`