demos for spacedust
1import { useContext, useEffect, useState } from 'react';
2
3const loadingDefault = () => (
4 <em>Loading…</em>
5);
6
7const errorDefault = err => (
8 <span className="error">
9 <strong>Error</strong>:<br/>{`${err}`}
10 </span>
11);
12
13export function Fetch({ using, args, ok, loading, error }) {
14 const [asyncData, setAsyncData] = useState({ state: null });
15
16 useEffect(() => {
17 let ignore = false;
18 setAsyncData({ state: 'loading' });
19 (async () => {
20 try {
21 const data = await using(...args);
22 !ignore && setAsyncData({ state: 'done', data });
23 } catch (err) {
24 !ignore && setAsyncData({ state: 'error', err });
25 }
26 })();
27 return () => { ignore = true; }
28 }, args);
29
30 if (asyncData.state === 'loading') {
31 return (loading || loadingDefault)(...args);
32 } else if (asyncData.state === 'error') {
33 return (error || errorDefault)(asyncData.err);
34 } else if (asyncData.state === null) {
35 return <span>wat, request has not started (bug?)</span>;
36 } else {
37 if (asyncData.state !== 'done') { console.warn(`unexpected async data state: ${asyncData.state}`); }
38 return ok(asyncData.data);
39 }
40}
41
42/////
43
44async function getJson(url, credentials) {
45 const opts = {};
46 if (credentials) opts.credentials = 'include';
47 const res = await fetch(url, opts);
48 if (!res.ok) {
49 const m = await res.text();
50 throw new Error(`Failed to fetch: ${m}`);
51 }
52 return await res.json();
53}
54
55export function GetJson({ endpoint, params, credentials, ...forFetch }) {
56 const host = import.meta.env.VITE_NOTIFICATIONS_HOST;
57 const url = new URL(endpoint, host);
58 for (let [key, val] of Object.entries(params ?? {})) {
59 url.searchParams.append(key, val);
60 }
61 return (
62 <Fetch
63 using={getJson}
64 args={[url.toString(), credentials]}
65 {...forFetch}
66 />
67 );
68}
69
70export async function postJson(url, body, credentials) {
71 const opts = {
72 method: 'POST',
73 headers: {'Content-Type': 'applicaiton/json'},
74 body,
75 };
76 if (credentials) opts.credentials = 'include';
77 const res = await fetch(url, opts);
78 if (!res.ok) {
79 const m = await res.text();
80 let reason
81 try {
82 reason = JSON.parse(m)?.reason;
83 } catch (err) {};
84 if (reason) throw reason;
85 throw new Error(`Failed to fetch: ${m}`);
86 }
87 try {
88 return await res.json();
89 } catch (e) {
90 if ([201, 204].includes(res.status)) return null;
91 throw e;
92 }
93}
94
95export function PostJson({ endpoint, data, credentials, ...forFetch }) {
96 const host = import.meta.env.VITE_NOTIFICATIONS_HOST;
97 const url = new URL(endpoint, host);
98 return (
99 <Fetch
100 using={postJson}
101 args={[url.toString(), JSON.stringify(data), credentials]}
102 {...forFetch}
103 />
104 );
105}