-
Notifications
You must be signed in to change notification settings - Fork 27
/
index.ts
108 lines (95 loc) · 2.35 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
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
101
102
103
104
105
106
107
108
import {
Behavior,
changes,
Now,
performStreamLatest,
sample,
split,
stepper,
Stream
} from "@funkia/hareactive";
import {
catchE,
combine,
Either,
IO,
left,
right,
withEffectsP
} from "@funkia/jabz";
import {
Component,
elements,
modelView,
runComponent,
fgo
} from "../../src/index";
const { span, br, input } = elements;
const apiUrl = "http://api.zippopotam.us/us/";
const fetchJSON = withEffectsP(
(url: string): Promise<any> => {
return fetch(url).then(
(resp) => (resp.ok ? resp.json() : Promise.reject("Not found"))
);
}
);
function fetchZip(zip: string): IO<Either<string, string>> {
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 ToView = {
status: Behavior<string>;
};
type ViewOut = {
zipCode: Behavior<string>;
};
const model = fgo(function*({ zipCode }: ViewOut): Iterator<Now<any>> {
// A stream that occurs whenever the current zip code changes
const zipCodeChange = changes(zipCode);
// Split the zip code changes into those that represent valid zip
// codes and those that represent invalid zip codes.
const [validZipCodeChange, invalidZipCodeChange] = 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: Stream<Either<string, ZipResult>> = yield performStreamLatest(
requests
);
const status = yield sample(
stepper(
"",
combine(
invalidZipCodeChange.mapTo("Not a valid zip code"),
validZipCodeChange.mapTo("Loading ..."),
results.map((r) =>
r.match({
left: () => "Zip code does not exist",
right: (res) => `Valid zip code for ${res.places[0]["place name"]}`
})
)
)
)
);
return { status };
});
const view = ({ status }: ToView) => [
span("Please type a valid US zip code: "),
input({
props: { placeholder: "Zip code" }
}).output({ zipCode: "inputValue" }),
br,
span(status)
];
const main = modelView<ToView, ViewOut>(model, view)();
runComponent("#mount", main);