on bdcbeba help sign in
bridge/src/store/lib/useHostingStore.js
234 lines 6.0 kB history raw
1import { useState, useCallback, useMemo } from 'react';
2import { Nothing } from 'folktale/maybe';
3import * as ob from 'urbit-ob';
5import { getVaneName, getVaneNumber } from 'lib/hosting';
6import SolarisClient from 'lib/SolarisClient';
8import { usePointCursor } from 'store/pointCursor';
10const STATE = {
11 UNKNOWN: 'UNKNOWN',
12 MISSING: 'MISSING',
13 RUNNING: 'RUNNING',
14 PENDING: 'PENDING',
15};
17function useHostingStore(url) {
18 const client = new SolarisClient(url);
20 const { pointCursor } = usePointCursor();
22 const [bootProgress, setBootProgress] = useState(0);
23 const [bootMessage, setBootMessage] = useState('Assembling Urbit');
24 const [startTime] = useState(Date.now());
26 const [status, _setStatus] = useState(STATE.UNKNOWN);
27 const setStatus = useCallback(
28 s => {
29 console.log(s);
30 _setStatus(s);
31 },
32 [_setStatus]
33 );
35 const [sysEvents, setSysEvents] = useState([]);
36 const [newEvents, setNewEvents] = useState([]);
37 const [runEvents, setRunEvents] = useState([]);
39 const [error, _setError] = useState();
40 const setError = useCallback(
41 error => {
42 _setError(error);
43 if (error) {
44 setStatus(STATE.UNKNOWN);
45 console.error(error);
46 }
47 },
48 [_setError, setStatus]
49 );
51 const unknown = status === STATE.UNKNOWN;
52 const missing = status === STATE.MISSING;
53 const running = status === STATE.RUNNING;
54 const pending = status === STATE.PENDING;
56 let syncStatus;
58 const resetEvents = useCallback(() => {
59 setSysEvents([]);
60 setNewEvents([]);
61 setRunEvents([]);
62 }, [setSysEvents, setNewEvents, setRunEvents]);
64 const getEvents = useCallback(() => {
65 try {
66 if (Nothing.hasInstance(pointCursor)) {
67 return;
68 }
69 const patp = ob.patp(pointCursor.value);
70 setError(undefined);
72 resetEvents();
74 const url = client.getShipsByPatpUrl(patp) + '/events';
75 const source = new EventSource(url);
77 // source.onerror = () => setStatus(STATE.UNKNOWN);
78 // source.onmessage = console.log;
80 source.addEventListener('sys', event => {
81 // console.log(event);
82 const sysEvent = JSON.parse(event.data);
83 const time = new Date(sysEvent.object.firstTimestamp).getTime();
84 if (time > startTime) {
85 console.log(sysEvent);
86 setSysEvents(previous => [...previous, sysEvent]);
87 }
88 });
90 source.addEventListener('new', event => {
91 // console.log(event);
92 const elapsed = (Date.now() - startTime) / 1000;
93 if (event.data !== '') {
94 setNewEvents(previous => [...previous, event.data]);
95 }
96 if (event.data === '1-b') {
97 setBootMessage('Compiling Hoon');
98 setBootProgress(0.2);
99 console.log(`${elapsed}: compiling Hoon`);
100 } else if (event.data === '1-c') {
101 setBootProgress(0.25);
102 } else if (event.data === '%arvo-assembly') {
103 setBootMessage('Compiling Urbit OS');
104 setBootProgress(0.3);
105 } else if (event.data.startsWith('[%vane %')) {
106 const vane = getVaneName(event.data.slice(8, 9));
107 setBootMessage(`Compiling vane: ${vane}`);
108 setBootProgress(0.3 + 0.05 * getVaneNumber(event.data.slice(8, 9)));
109 } else if (event.data === 'pier: boot complete') {
110 setBootMessage('Ship starting');
111 setBootProgress(0.85);
112 }
113 });
115 source.addEventListener('run', event => {
116 if (event.data === 'goad: recompiling all apps') {
117 setBootProgress(1.0);
118 setBootMessage('Ship Launched');
119 syncStatus();
120 }
121 if (event.data !== '') {
122 setRunEvents(previous => [...previous, event.data]);
123 }
124 });
125 } catch (error) {
126 setError(error);
127 }
128 }, [
129 setError,
130 resetEvents,
131 client,
132 pointCursor,
133 setBootMessage,
134 startTime,
135 syncStatus,
136 ]);
138 syncStatus = useCallback(async () => {
139 try {
140 if (Nothing.hasInstance(pointCursor)) {
141 return;
142 }
143 const patp = ob.patp(pointCursor.value);
144 setError(undefined);
146 resetEvents();
148 const response = await client.getShipsByPatp(patp);
150 if (response.status === 404) {
151 setStatus(STATE.MISSING);
152 } else if (!response.ok) {
153 setError(response.statusText);
154 } else {
155 const ship = await response.json();
157 if (ship.status === 'Pending') {
158 setStatus(STATE.PENDING);
159 getEvents();
160 } else if (ship.status === 'Running') {
161 setStatus(STATE.RUNNING);
162 } else {
163 setError(ship);
164 }
165 }
166 } catch (error) {
167 setError(error);
168 }
169 }, [client, getEvents, pointCursor, resetEvents, setError, setStatus]);
171 const hostedShipUrl = useMemo(
172 () =>
173 `http://${ob.patp(pointCursor.getOrElse('')).slice(1)}.liam.tlon.network`,
174 [pointCursor]
175 );
177 const create = useCallback(
178 async keyfile => {
179 try {
180 if (Nothing.hasInstance(pointCursor)) {
181 return;
182 }
183 const patp = ob.patp(pointCursor.value).slice(1);
184 setError(undefined);
186 const start = Date.now();
187 console.log(start);
188 setStatus(STATE.PENDING);
189 setBootProgress(0.1);
191 const response = await client.postShips({
192 patp,
193 key: keyfile,
194 debug: true,
195 fake: false,
196 });
198 if (!response.ok) {
199 setError(response.statusText);
200 } else {
201 getEvents();
202 }
203 } catch (error) {
204 setError(error);
205 }
206 },
207 [pointCursor, setError, setStatus, client, getEvents]
208 );
210 return {
211 error,
212 unknown,
213 missing,
214 running,
215 pending,
216 url: hostedShipUrl,
218 sysEvents,
219 newEvents,
220 runEvents,
222 syncStatus,
223 getEvents,
224 create,
226 bootProgress,
227 bootMessage,
229 // debugging
230 status,
231 };
234export default useHostingStore;