-
Notifications
You must be signed in to change notification settings - Fork 27
/
index.ts
73 lines (63 loc) · 1.99 KB
/
index.ts
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
import * as H from "@funkia/hareactive";
import { combine, Either, left, right } from "@funkia/jabz";
import { withEffectsP, catchE, IO } from "@funkia/io";
import { elements as E, runComponent, component } from "../../src/index";
const apiUrl = "http://api.zippopotam.us/us/";
const fetchJSON = withEffectsP((url: string) =>
fetch(url).then((resp) =>
resp.ok ? resp.json() : Promise.reject("Not found")
)
);
function fetchZip(zip: string): IO<Either<string, ZipResult>> {
return catchE((err) => IO.of(left(err)), fetchJSON(apiUrl + zip).map(right));
}
const isValidZip = (s: string) => s.match(/^\d{5}$/) !== null;
type Place = {
"place name": string;
state: string;
};
type ZipResult = {
country: string;
places: Place[];
};
type ViewOut = {
zipCode: H.Behavior<string>;
};
const main = component<ViewOut>((on, start) => {
const zipCodeChange = H.changes(on.zipCode);
// Split the zip code changes into those that represent valid zip
// codes and those that represent invalid zip codes.
const [validZipCodeChange, invalidZipCodeChange] = H.split(
isValidZip,
zipCodeChange
);
// A stream of IO requests for each time the zipCode changes
const requests = validZipCodeChange.map(fetchZip);
// A stream of results obtained from performing the IO requests
const results = start(H.performStream(requests));
const latestResult = start(H.flatFuturesLatest(results));
const status = start(
H.stepper(
"",
combine(
invalidZipCodeChange.mapTo("Not a valid zip code"),
validZipCodeChange.mapTo("Loading ..."),
latestResult.map((r) =>
r.match({
left: () => "Zip code does not exist",
right: (res) => `Valid zip code for ${res.places[0]["place name"]}`,
})
)
)
)
);
return [
E.span("Please type a valid US zip code: "),
E.input({
props: { placeholder: "Zip code" },
}).use({ zipCode: "value" }),
E.br,
E.span(status),
];
});
runComponent("#mount", main);