forked from yashton/terpstrakeyboard
-
Notifications
You must be signed in to change notification settings - Fork 3
/
use-query.js
123 lines (105 loc) · 3.14 KB
/
use-query.js
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
export class Extract {
constructor(from, to) {
this.to = to;
this.from = from;
}
extract(query, key) {
if (query.has(key)) {
return this.from(query.get(key));
} else {
return null;
}
}
insert(query, key, value) {
query.set(key, this.to(value));
}
restore(key) {
return this.from(localStorage.getItem(key));
}
store(key, value) {
localStorage.setItem(key, this.to(value));
}
}
export class ExtractArray {
constructor(from, to) {
this.to = to;
this.from = from;
}
extract(query, key) {
if (query.has(key)) {
return query.getAll(key).map(this.from);
} else {
return null;
}
}
insert(query, key, values) {
values.map(this.to).forEach(v => query.append(key, v));
}
restore(key) {
return null; // TODO
}
store(key, value) {
return null; // TODO
}
}
export const ExtractString = new Extract(x => x, x => x);
export const ExtractStringArray = new ExtractArray(x => x, x => x);
export const ExtractJoinedString = new Extract(x => x.split(","), x => x.join(","));
export const ExtractFloat = new Extract(x => Number.parseFloat(x), x => x.toString());
export const ExtractFloatArray = new Extract(x => Number.parseFloat(x), x => x.toString());
export const ExtractInt = new Extract(x => Number.parseInt(x), x => x.toString());
export const ExtractIntArray = new Extract(x => Number.parseInt(x), x => x.toString());
export const ExtractBool = new Extract(x => x === "true", x => x.toString());
export const ExtractBoolArray = new Extract(x => x === "true", x => x.toString());
export function useQuery(spec, defaults) {
const initial = {};
if (document.location.search.length > 0) {
const query = new URLSearchParams(document.location.search.substring(1));
for (let [key, extract] of Object.entries(spec)) {
if (query.has(key)) {
initial[key] = extract.extract(query, key);
}
}
} else {
for (let [key, extract] of Object.entries(spec)) {
if (localStorage.getItem(key)) {
initial[key] = extract.restore(key);
}
}
}
const [values, setValues] = useState(initial);
function handle(e) {
const query = new URLSearchParams(document.location.search.substring(1));
const output = {};
for (let [key, extract] of Object.entries(spec)) {
if (query.has(key)) {
output[key] = extract.extract(query, key);
}
}
setValues(output);
}
function setState(next_f) {
const query = new URLSearchParams();
const next = next_f(values);
for (let [key, extract] of Object.entries(spec)) {
if (key in next && next[key]) {
extract.insert(query, key, next[key]);
extract.store(key, next[key]);
}
}
const url = new URL(location.toString());
url.search = query.toString();
history.pushState({}, "Hexatone WebApp", url);
setValues(next);
}
useEffect(() => {
window.addEventListener('popstate', handle);
return () => {
window.removeEventListener('popstate', handle);
};
}, []);
return [values, setState];
}
export default useQuery;