Public
HTTP
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/react */
import copy from "https://esm.sh/copy-text-to-clipboard";
import leven from "https://esm.sh/fast-levenshtein";
import { useEffect, useState } from "https://esm.sh/react";
import { DataRequest, render } from "https://esm.town/v/jxnblk/ReactStream";
import { useStreamingAPI } from "https://esm.town/v/jxnblk/useStreamingAPI";
// chat and encode/decode
import api, { decode } from "https://esm.town/v/jxnblk/IndirectionAPI";
import { GoogleFonts } from "https://esm.town/v/jxnblk/reactGoogleFonts";
import { Typewriter } from "https://esm.town/v/jxnblk/useTypewriter";
const PERMALINK = "https://www.val.town/v/jxnblk/indirection";
function App(props) {
const [history, setHistory] = useState<Turn[]>(props.data.history || []);
const [word, setWord] = useState<string>("");
const [turn, setTurn] = useState(props.data.turn || emptyTurn);
const [ui, setUI] = useState<UIState>(props.data?.turn?.clue ? UIState.Guessing : UIState.Start);
const { message, submit, loading, error } = useStreamingAPI<APIRequestBody>();
const addHistoryGuess = (guess: string) => {
setHistory([
...history,
{
word: turn.word,
clue: turn.clue,
guess: guess,
},
]);
};
const handleSubmit = e => {
e.preventDefault();
let correct = null;
if (ui === UIState.Guessing) {
// is a guess play
addHistoryGuess(word.toLowerCase());
correct = turn.word === word.toLowerCase();
console.log("levenshtein", leven.get(turn.word, word.toLowerCase()));
setTurn({ ...turn, guess: word.toLowerCase() });
} else if (ui === UIState.Correct) {
// is a new word after correct answer
submit({ word: word.toLowerCase() });
setUI(UIState.Loading);
return;
}
if (correct) {
setUI(UIState.Correct);
setWord("");
} else {
// incorrect words continue the chain
submit({ word: word.toLowerCase() });
setUI(UIState.Loading);
}
};
const copyLink = () => {
if (copy(window.location.href)) {
console.log("copied");
}
};
useEffect(() => {
if (loading) return;
if (!message) return;
console.log({ word, message });
setURL({
history,
turn: { // for the next play / page load
word: word.toLowerCase(),
clue: message,
},
});
setUI(UIState.Sharing);
}, [word, message, loading]);
const showForm = ui === UIState.Start || ui === UIState.Guessing || ui === UIState.Correct;
const urlLength = props.url?.length || -1;
console.log("url.length", urlLength);
return (
<html>
<head>
<OGMeta {...turn} url={props.url.href} />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<GoogleFonts family="Lexend" />
<GoogleFonts family="BioRhyme" weight="400" />
<GoogleFonts family="BioRhyme" weight="800" />
<style dangerouslySetInnerHTML={{ __html: css }} />
</head>
<body>
<div className="container card">
{error && (
<div>
Something went wrong. Reload the page and try again.
</div>
)}
<h1 className="h1 slab xbold">Indirection</h1>
<>