on master help sign in
address PR feedback
83e8ca4Liam Fitzgerald
src/lib/SolarisClient.js+15−16
@@ -10,97 +10,96 @@ export default class SolarisClient {
10 }
10 }
11
11
12 getHealthzUrl() {
12 getHealthzUrl() {
13− return (this.endpoint + '/healthz');
13+ return this.endpoint + '/healthz';
14 }
14 }
15
15
16 async getHealthz() {
16 async getHealthz() {
17 return await fetch(this.getHealthzUrl(), {
17 return await fetch(this.getHealthzUrl(), {
18 method: 'GET',
18 method: 'GET',
19 headers: {
19 headers: {
20− 'Accept': 'application/json',
20+ Accept: 'application/json',
21 },
21 },
22 });
22 });
23 }
23 }
24
24
25 getUrl() {
25 getUrl() {
26− return (this.endpoint + '/');
26+ return this.endpoint + '/';
27 }
27 }
28
28
29 async get() {
29 async get() {
30 return await fetch(this.getUrl(), {
30 return await fetch(this.getUrl(), {
31 method: 'GET',
31 method: 'GET',
32 headers: {
32 headers: {
33− 'Accept': 'application/json',
33+ Accept: 'application/json',
34 },
34 },
35 });
35 });
36 }
36 }
37
37
38 getImagesUrl() {
38 getImagesUrl() {
39− return (this.endpoint + '/images');
39+ return this.endpoint + '/images';
40 }
40 }
41
41
42 async getImages() {
42 async getImages() {
43 return await fetch(this.getImagesUrl(), {
43 return await fetch(this.getImagesUrl(), {
44 method: 'GET',
44 method: 'GET',
45 headers: {
45 headers: {
46− 'Accept': 'application/json',
46+ Accept: 'application/json',
47 },
47 },
48 });
48 });
49 }
49 }
50
50
51 getShipsUrl() {
51 getShipsUrl() {
52− return (this.endpoint + '/ships');
52+ return this.endpoint + '/ships';
53 }
53 }
54
54
55 async getShips() {
55 async getShips() {
56 return await fetch(this.getShipsUrl(), {
56 return await fetch(this.getShipsUrl(), {
57 method: 'GET',
57 method: 'GET',
58 headers: {
58 headers: {
59− 'Accept': 'application/json',
59+ Accept: 'application/json',
60 },
60 },
61 });
61 });
62 }
62 }
63
63
64 getShipsByPatpUrl(patp) {
64 getShipsByPatpUrl(patp) {
65− return (this.endpoint + '/ships/' + encodeURIComponent(patp) + '');
65+ return this.endpoint + '/ships/' + encodeURIComponent(patp) + '';
66 }
66 }
67
67
68 async getShipsByPatp(patp) {
68 async getShipsByPatp(patp) {
69 return await fetch(this.getShipsByPatpUrl(patp), {
69 return await fetch(this.getShipsByPatpUrl(patp), {
70 method: 'GET',
70 method: 'GET',
71 headers: {
71 headers: {
72− 'Accept': 'application/json',
72+ Accept: 'application/json',
73 },
73 },
74 });
74 });
75 }
75 }
76
76
77 getShipsByPatpDetailsUrl(patp) {
77 getShipsByPatpDetailsUrl(patp) {
78− return (this.endpoint + '/ships/' + encodeURIComponent(patp) + '/details');
78+ return this.endpoint + '/ships/' + encodeURIComponent(patp) + '/details';
79 }
79 }
80
80
81 async getShipsByPatpDetails(patp) {
81 async getShipsByPatpDetails(patp) {
82 return await fetch(this.getShipsByPatpDetailsUrl(patp), {
82 return await fetch(this.getShipsByPatpDetailsUrl(patp), {
83 method: 'GET',
83 method: 'GET',
84 headers: {
84 headers: {
85− 'Accept': 'application/json',
85+ Accept: 'application/json',
86 },
86 },
87 });
87 });
88 }
88 }
89
89
90 postShipsUrl() {
90 postShipsUrl() {
91− return (this.endpoint + '/ships');
91+ return this.endpoint + '/ships';
92 }
92 }
93
93
94 async postShips(data) {
94 async postShips(data) {
95 return await fetch(this.postShipsUrl(), {
95 return await fetch(this.postShipsUrl(), {
96 method: 'POST',
96 method: 'POST',
97 cache: 'no-cache',
97 cache: 'no-cache',
98− body: JSON.stringify({...data, debug: true}),
98+ body: JSON.stringify(data),
99 headers: {
99 headers: {
100− 'Accept': 'application/json',
100+ Accept: 'application/json',
101 'Content-Type': 'application/json',
101 'Content-Type': 'application/json',
102 },
102 },
103 });
103 });
104 }
104 }
105−
106}
105}
src/store/hosting.js+1−1
@@ -5,7 +5,7 @@ import useHostingStore from './lib/useHostingStore';
5export const HostingContext = createContext(null);
5export const HostingContext = createContext(null);
6
6
7export function HostingProvider({ children }) {
7export function HostingProvider({ children }) {
8− const store = useHostingStore('http://localhost:8080');
8+ const store = useHostingStore('http://localhost:4000');
9 return (
9 return (
10 <HostingContext.Provider value={store}>{children}</HostingContext.Provider>
10 <HostingContext.Provider value={store}>{children}</HostingContext.Provider>
11 );
11 );
src/store/lib/useHostingStore.js+6
@@ -104,6 +104,10 @@ function useHostingStore(url) {
104 }
104 }
105 }, [client, pointCursor, resetEvents, setError, setState]);
105 }, [client, pointCursor, resetEvents, setError, setState]);
106
106
107+ useEffect(() => {
108+ syncStatus();
109+ }, [pointCursor, syncStatus]);
110+
107 const getEvents = useCallback(() => {
111 const getEvents = useCallback(() => {
108 try {
112 try {
109 if (Nothing.hasInstance(pointCursor)) {
113 if (Nothing.hasInstance(pointCursor)) {
@@ -160,6 +164,8 @@ function useHostingStore(url) {
160 const response = await client.postShips({
164 const response = await client.postShips({
161 patp,
165 patp,
162 key: keyfile,
166 key: keyfile,
167+ debug: true,
168+ fake: false,
163 });
169 });
164
170
165 if (!response.ok) {
171 if (!response.ok) {
src/views/UrbitOS/Home.js+119−51
@@ -1,4 +1,4 @@
1−import React, { useCallback, useState } from 'react';
1+import React, { useCallback, useState, useEffect } from 'react';
2import { Grid, Button, SelectInput } from 'indigo-react';
2import { Grid, Button, SelectInput } from 'indigo-react';
3import * as ob from 'urbit-ob';
3import * as ob from 'urbit-ob';
4import { azimuth } from 'azimuth-js';
4import { azimuth } from 'azimuth-js';
@@ -14,12 +14,15 @@ import {
14 OutButton,
14 OutButton,
15 ForwardButton,
15 ForwardButton,
16 BootUrbitOSButton,
16 BootUrbitOSButton,
17+ GenerateButton,
17} from 'components/Buttons';
18} from 'components/Buttons';
18import CopyButton from 'components/CopyButton';
19import CopyButton from 'components/CopyButton';
19import NetworkingKeys from 'components/NetworkingKeys';
20import NetworkingKeys from 'components/NetworkingKeys';
20
21
21import BridgeForm from 'form/BridgeForm';
22import BridgeForm from 'form/BridgeForm';
22import { useLocalRouter } from 'lib/LocalRouter';
23import { useLocalRouter } from 'lib/LocalRouter';
24+import { useHosting } from 'store/hosting';
25+import DownloadKeyfileButton from 'components/DownloadKeyfileButton';
23
26
24export default function UrbitOSHome() {
27export default function UrbitOSHome() {
25 const { pointCursor } = usePointCursor();
28 const { pointCursor } = usePointCursor();
@@ -32,8 +35,6 @@ export default function UrbitOSHome() {
32
35
33 const sponsor = ob.patp(details.sponsor);
36 const sponsor = ob.patp(details.sponsor);
34
37
35− const { code, notice } = useKeyfileGenerator();
36−
37 const [showKeys, setShowKeys] = useState(false);
38 const [showKeys, setShowKeys] = useState(false);
38
39
39 const showSponsor = azimuth.getPointSize(point) !== azimuth.PointSize.Galaxy;
40 const showSponsor = azimuth.getPointSize(point) !== azimuth.PointSize.Galaxy;
@@ -53,13 +54,7 @@ export default function UrbitOSHome() {
53 ]);
54 ]);
54 return (
55 return (
55 <>
56 <>
56− <Grid>
57+ <Hosting />
57− <Grid.Item full className="mv7">
58− Urbit OS
59− </Grid.Item>
60− <Grid.Divider />
61− <Grid.Item full as={BootUrbitOSButton} />
62− </Grid>
63
58
64 <Grid>
59 <Grid>
65 <Grid.Item full className="mv7 f5">
60 <Grid.Item full className="mv7 f5">
@@ -80,15 +75,7 @@ export default function UrbitOSHome() {
80 <Grid.Divider />
75 <Grid.Divider />
81 </>
76 </>
82 )}
77 )}
83− <Grid.Item
78+
84− full
85− as={ForwardButton}
86− accessory={code && <CopyButton text={code} />}
87− detail={code || notice}
88− disabled={!code}
89− detailClassName="mono">
90− Login Code
91− </Grid.Item>
92 <Grid.Divider />
79 <Grid.Divider />
93 <Grid.Item full as={ForwardButton} onClick={goNetworkingKeys}>
80 <Grid.Item full as={ForwardButton} onClick={goNetworkingKeys}>
94 Reset Networking Keys
81 Reset Networking Keys
@@ -109,52 +96,133 @@ export default function UrbitOSHome() {
109
96
110// eslint-disable-next-line
97// eslint-disable-next-line
111function Hosting() {
98function Hosting() {
99+ const bind = useKeyfileGenerator();
100+ const { keyfile, code, notice } = bind;
101+ const ship = { connected: true }; //useHosting();
102+
103+ const { syncStatus } = ship;
104+
105+ const createShip = useCallback(() => ship.create(keyfile), [keyfile, ship]);
106+
112 const name = useCurrentPointName();
107 const name = useCurrentPointName();
113 const options = [{ text: 'Tlon', value: 'tlon' }];
108 const options = [{ text: 'Tlon', value: 'tlon' }];
114
109
115− const props = { name, options };
110+ const props = { name, options, ship };
116− const connected = false;
117−
118− return (
119− <Grid gap={4}>
120− <Grid.Item full className="f5">
121− Urbit OS
122− </Grid.Item>
123
111
112+ const renderMain = useCallback(() => {
124− {(connected && <HostingConnected {...props} />) || (
113+ if (ship.connected) {
114+ return (
115+ <>
116+ <Grid.Item cols={[1, 9]} as={OutButton} solid success>
117+ Open OS
118+ </Grid.Item>
119+ <Grid.Item cols={[9, 13]} as={Button} className="b-black b1" center>
120+ Disconnect
121+ </Grid.Item>
122+ </>
123+ );
124+ }
125+ if (ship.missing || ship.booting) {
126+ return (
127+ <Grid.Item
128+ full
129+ as={GenerateButton}
130+ solid
131+ success
132+ disabled={ship.booting || !keyfile}
133+ loading={ship.booting}
134+ onClick={createShip}>
135+ Boot Urbit OS
136+ </Grid.Item>
137+ );
138+ }
139+ if (ship.running || ship.connecting) {
125− <HostingDisconnected {...props} />
140+ return <HostingDisconnected {...props} />;
141+ }
126− )}
142+ }, [
127− </Grid>
143+ ship.connected,
144+ ship.missing,
145+ ship.booting,
146+ ship.running,
147+ ship.connecting,
148+ keyfile,
149+ createShip,
150+ props,
128− );
151+ ]);
129−}
130
152
131−function HostingConnected({ name, options }) {
153+ const renderDetails = useCallback(() => {
132− return (
154+ if (ship.connected) {
133− <BridgeForm initialValues={{ provider: 'tlon' }}>
155+ return (
134− {() => (
135 <>
156 <>
136 <Grid.Item full>Status: Connected</Grid.Item>
157 <Grid.Item full>Status: Connected</Grid.Item>
137 <Grid.Item cols={[1, 9]} className="gray4">
158 <Grid.Item cols={[1, 9]} className="gray4">
138 <span className="mono">{name}</span> is connected to Tlon and has
159 <span className="mono">{name}</span> is connected to Tlon and has
139 the IP address 127.0.0.1
160 the IP address 127.0.0.1
140 </Grid.Item>
161 </Grid.Item>
141− <Grid.Item cols={[1, 9]} as={OutButton} solid success>
162+ </>
142− Open OS
163+ );
164+ }
165+ if (ship.running || ship.connected) {
166+ return (
167+ <>
168+ <Grid.Item full className="gray4">
169+ <span className="gray3">Status:</span> Disconnected
170+ <br />
171+ <span className="f6">Last connected: 4m ago</span>
143 </Grid.Item>
172 </Grid.Item>
144− <Grid.Item cols={[9, 13]} as={Button} className="b-black b1" center>
173+
145− Disconnect
174+ <Grid.Item full as={ForwardButton} solid>
175+ Connect
146 </Grid.Item>
176 </Grid.Item>
147− <Grid.Item
148− full
149− as={SelectInput}
150− name="provider"
151− label="Host Provider"
152− options={options}
153− disabled
154− />
155 </>
177 </>
178+ );
156− )}
179+ }
180+ }, [name, ship]);
181+
182+ return (
183+ <>
184+ <Grid gap={4}>
185+ <Grid.Item full className="f5">
186+ Urbit OS
187+ </Grid.Item>
157− </BridgeForm>
188+ <BridgeForm initialValues={{ provider: 'tlon' }}>
189+ {() => (
190+ <>
191+ {renderDetails()}
192+ {renderMain()}
193+ <Grid.Item
194+ full
195+ as={SelectInput}
196+ name="provider"
197+ label="Host Provider"
198+ options={options}
199+ disabled
200+ />
201+ </>
202+ )}
203+ </BridgeForm>
204+ </Grid>
205+ <Grid>
206+ <Grid.Item
207+ full
208+ as={DownloadKeyfileButton}
209+ {...bind}
210+ className="mt2"
211+ detail="A keyfile authenticates your Urbit ID to Urbit OS"
212+ />
213+ <Grid.Divider />
214+ <Grid.Item
215+ full
216+ className="mt2"
217+ as={ForwardButton}
218+ accessory={code && <CopyButton text={code} />}
219+ detail={code || notice}
220+ disabled={!code}
221+ detailClassName="mono">
222+ Login Code
223+ </Grid.Item>
224+ </Grid>
225+ </>
158 );
226 );
159}
227}
160
228
src/views/UrbitOS/NetworkingKeys.js+4−17
@@ -197,27 +197,14 @@ export default function UrbitOSNetworkingKeys() {
197
197
198 const usageMessage = 'You need this to authenticate with arvo';
198 const usageMessage = 'You need this to authenticate with arvo';
199
199
200− const renderTitle = () => {
201− if (completed) {
202− return 'Networking keys are now set.';
203− }
204−
205− if (broadcasting) {
206− return 'Setting Network Keys...';
207− }
208−
209− if (!hasKeys) {
210− return 'Networking keys have not yet been set.';
211− }
212−
213− return 'Networking';
214− };
215−
216 return (
200 return (
217 <>
201 <>
218 <Grid>
202 <Grid>
219 <Grid.Item full as={ViewHeader}>
203 <Grid.Item full as={ViewHeader}>
220− {renderTitle()}
204+ Set Networking Keys
205+ </Grid.Item>
206+ <Grid.Item className="gray4 f6" full>
207+ Network Keys solidify the handshake between ID and OS
221 </Grid.Item>
208 </Grid.Item>
222 {completed && (
209 {completed && (
223 <>
210 <>