Skip to content

Commit 1f54bb7

Browse files
committed
Refactor register
1 parent 2a2e678 commit 1f54bb7

File tree

8 files changed

+419
-366
lines changed

8 files changed

+419
-366
lines changed

data-browser/src/components/Guard.tsx

Lines changed: 3 additions & 361 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,6 @@
1-
import {
2-
Dialog,
3-
DialogActions,
4-
DialogContent,
5-
DialogTitle,
6-
useDialog,
7-
} from './Dialog';
8-
import React, { FormEvent, useCallback, useEffect, useState } from 'react';
1+
import React from 'react';
92
import { useSettings } from '../helpers/AppSettings';
10-
import { Button, ButtonInput } from './Button';
11-
import { Agent, nameRegex, useRegister, useServerURL } from '@tomic/react';
12-
import { FaEyeSlash, FaEye, FaCog } from 'react-icons/fa';
13-
import Field from './forms/Field';
14-
import { InputWrapper, InputStyled } from './forms/InputStyles';
15-
import { Row } from './Row';
16-
import { ErrorLook } from './ErrorLook';
17-
import { CodeBlock } from './CodeBlock';
3+
import { RegisterSignIn } from './RegisterSignIn';
184

195
/**
206
* The Guard can be wrapped around a Component that depends on a user being logged in.
@@ -23,353 +9,9 @@ import { CodeBlock } from './CodeBlock';
239
* Instructs them to save their secret somewhere safe
2410
*/
2511
export function Guard({ children }: React.PropsWithChildren<any>): JSX.Element {
26-
const { dialogProps, show } = useDialog();
2712
const { agent } = useSettings();
28-
const [register, setRegister] = useState(true);
2913

3014
if (agent) {
3115
return <>{children}</>;
32-
} else
33-
return (
34-
<>
35-
<Row>
36-
<Button
37-
onClick={() => {
38-
setRegister(true);
39-
show();
40-
}}
41-
>
42-
Register
43-
</Button>
44-
<Button
45-
subtle
46-
onClick={() => {
47-
setRegister(false);
48-
show();
49-
}}
50-
>
51-
Sign In
52-
</Button>
53-
</Row>
54-
<Dialog {...dialogProps}>{register ? <Register /> : <SignIn />}</Dialog>
55-
</>
56-
);
16+
} else return <RegisterSignIn />;
5717
}
58-
59-
function Register() {
60-
const [name, setName] = useState('');
61-
const [secret, setSecret] = useState('');
62-
const [driveURL, setDriveURL] = useState('');
63-
const [newAgent, setNewAgent] = useState<Agent | undefined>(undefined);
64-
const [serverUrlStr] = useServerURL();
65-
const [err, setErr] = useState<Error | undefined>(undefined);
66-
const register = useRegister();
67-
const { setAgent } = useSettings();
68-
69-
const serverUrl = new URL(serverUrlStr);
70-
serverUrl.host = `${name}.${serverUrl.host}`;
71-
72-
useEffect(() => {
73-
// check regex of name, set error
74-
if (!name.match(nameRegex)) {
75-
setErr(new Error('Name must be lowercase and only contain numbers'));
76-
} else {
77-
setErr(undefined);
78-
}
79-
}, [name]);
80-
81-
const handleSubmit = useCallback(
82-
async (event: FormEvent) => {
83-
event.preventDefault();
84-
85-
if (!name) {
86-
setErr(new Error('Name is required'));
87-
88-
return;
89-
}
90-
91-
try {
92-
const { driveURL: newDriveURL, agent } = await register(name);
93-
setDriveURL(newDriveURL);
94-
setSecret(agent.buildSecret());
95-
setNewAgent(agent);
96-
} catch (er) {
97-
setErr(er);
98-
}
99-
},
100-
[name],
101-
);
102-
103-
const handleSaveAgent = useCallback(() => {
104-
setAgent(newAgent);
105-
}, [newAgent]);
106-
107-
if (driveURL) {
108-
return (
109-
<>
110-
<DialogTitle>
111-
<h1>Save your Passphrase, {name}</h1>
112-
</DialogTitle>
113-
<DialogContent>
114-
<p>
115-
Your Passphrase is like your password. Never share it with anyone.
116-
Use a password manager to store it securely. You will need this to
117-
log in next!
118-
</p>
119-
<CodeBlock content={secret} wrapContent />
120-
</DialogContent>
121-
<DialogActions>
122-
<Button onClick={handleSaveAgent}>Continue here</Button>
123-
<a href={driveURL} target='_blank' rel='noreferrer'>
124-
Open my new Drive!
125-
</a>
126-
</DialogActions>
127-
</>
128-
);
129-
}
130-
131-
return (
132-
<>
133-
<DialogTitle>
134-
<h1>Register</h1>
135-
</DialogTitle>
136-
<DialogContent>
137-
<form onSubmit={handleSubmit} id='register-form'>
138-
<Field
139-
label='Unique username'
140-
helper='Becomes a part of your URL, e.g. `example.atomicdata.dev`'
141-
>
142-
<InputWrapper>
143-
<InputStyled
144-
autoFocus={true}
145-
pattern={nameRegex}
146-
type={'text'}
147-
required
148-
value={name}
149-
onChange={e => {
150-
setName(e.target.value);
151-
}}
152-
/>
153-
</InputWrapper>
154-
</Field>
155-
{!err && name?.length > 0 && <code>{serverUrl.toString()}</code>}
156-
{name && err && <ErrorLook>{err.message}</ErrorLook>}
157-
</form>
158-
</DialogContent>
159-
<DialogActions>
160-
<Button
161-
type='submit'
162-
form='register-form'
163-
disabled={!name || !!err}
164-
onClick={handleSubmit}
165-
>
166-
Create
167-
</Button>
168-
</DialogActions>
169-
</>
170-
);
171-
}
172-
173-
function SignIn() {
174-
return (
175-
<>
176-
<DialogTitle>
177-
<h1>Sign in </h1>
178-
</DialogTitle>
179-
<DialogContent>
180-
<SettingsAgent />
181-
<p>Lost your passphrase?</p>
182-
</DialogContent>
183-
</>
184-
);
185-
}
186-
187-
export const SettingsAgent: React.FunctionComponent = () => {
188-
const { agent, setAgent } = useSettings();
189-
const [subject, setSubject] = useState<string | undefined>(undefined);
190-
const [privateKey, setPrivateKey] = useState<string | undefined>(undefined);
191-
const [error, setError] = useState<Error | undefined>(undefined);
192-
const [showPrivateKey, setShowPrivateKey] = useState(false);
193-
const [advanced, setAdvanced] = useState(false);
194-
const [secret, setSecret] = useState<string | undefined>(undefined);
195-
196-
// When there is an agent, set the advanced values
197-
// Otherwise, reset the secret value
198-
React.useEffect(() => {
199-
if (agent !== undefined) {
200-
fillAdvanced();
201-
} else {
202-
setSecret('');
203-
}
204-
}, [agent]);
205-
206-
// When the key or subject changes, update the secret
207-
React.useEffect(() => {
208-
renewSecret();
209-
}, [subject, privateKey]);
210-
211-
function renewSecret() {
212-
if (agent) {
213-
setSecret(agent.buildSecret());
214-
}
215-
}
216-
217-
function fillAdvanced() {
218-
try {
219-
if (!agent) {
220-
throw new Error('No agent set');
221-
}
222-
223-
setSubject(agent.subject);
224-
setPrivateKey(agent.privateKey);
225-
} catch (e) {
226-
const err = new Error('Cannot fill subject and privatekey fields.' + e);
227-
setError(err);
228-
setSubject('');
229-
}
230-
}
231-
232-
function setAgentIfChanged(oldAgent: Agent | undefined, newAgent: Agent) {
233-
if (JSON.stringify(oldAgent) !== JSON.stringify(newAgent)) {
234-
setAgent(newAgent);
235-
}
236-
}
237-
238-
/** Called when the secret or the subject is updated manually */
239-
async function handleUpdateSubjectAndKey() {
240-
renewSecret();
241-
setError(undefined);
242-
243-
try {
244-
const newAgent = new Agent(privateKey!, subject);
245-
await newAgent.getPublicKey();
246-
await newAgent.verifyPublicKeyWithServer();
247-
248-
setAgentIfChanged(agent, newAgent);
249-
} catch (e) {
250-
const err = new Error('Invalid Agent' + e);
251-
setError(err);
252-
}
253-
}
254-
255-
function handleCopy() {
256-
secret && navigator.clipboard.writeText(secret);
257-
}
258-
259-
/** When the Secret updates, parse it and try if the */
260-
async function handleUpdateSecret(updateSecret: string) {
261-
setSecret(updateSecret);
262-
263-
if (updateSecret === '') {
264-
setSecret('');
265-
setError(undefined);
266-
267-
return;
268-
}
269-
270-
setError(undefined);
271-
272-
try {
273-
const newAgent = Agent.fromSecret(updateSecret);
274-
setAgentIfChanged(agent, newAgent);
275-
setPrivateKey(newAgent.privateKey);
276-
setSubject(newAgent.subject);
277-
// This will fail and throw if the agent is not public, which is by default
278-
// await newAgent.checkPublicKey();
279-
} catch (e) {
280-
const err = new Error('Invalid secret. ' + e);
281-
setError(err);
282-
}
283-
}
284-
285-
return (
286-
<form>
287-
<Field
288-
label={agent ? 'Passphrase' : 'Enter your Passphrase'}
289-
helper={
290-
"The Agent Passphrase is a secret, long string of characters that encodes both the Subject and the Private Key. You can think of it as a combined username + password. Store it safely, and don't share it with others."
291-
}
292-
error={error}
293-
>
294-
<InputWrapper>
295-
<InputStyled
296-
value={secret}
297-
onChange={e => handleUpdateSecret(e.target.value)}
298-
type={showPrivateKey ? 'text' : 'password'}
299-
disabled={agent !== undefined}
300-
name='secret'
301-
id='current-password'
302-
autoComplete='current-password'
303-
spellCheck='false'
304-
placeholder='Paste your Passphrase'
305-
/>
306-
<ButtonInput
307-
type='button'
308-
title={showPrivateKey ? 'Hide secret' : 'Show secret'}
309-
onClick={() => setShowPrivateKey(!showPrivateKey)}
310-
>
311-
{showPrivateKey ? <FaEyeSlash /> : <FaEye />}
312-
</ButtonInput>
313-
<ButtonInput
314-
type='button'
315-
title={advanced ? 'Hide advanced' : 'Show advanced'}
316-
onClick={() => setAdvanced(!advanced)}
317-
>
318-
<FaCog />
319-
</ButtonInput>
320-
{agent && (
321-
<ButtonInput type='button' onClick={handleCopy}>
322-
copy
323-
</ButtonInput>
324-
)}
325-
</InputWrapper>
326-
</Field>
327-
{advanced ? (
328-
<React.Fragment>
329-
<Field
330-
label='Subject URL'
331-
helper={
332-
'The link to your Agent, e.g. https://atomicdata.dev/agents/someAgent'
333-
}
334-
>
335-
<InputWrapper>
336-
<InputStyled
337-
disabled={agent !== undefined}
338-
value={subject}
339-
onChange={e => {
340-
setSubject(e.target.value);
341-
handleUpdateSubjectAndKey();
342-
}}
343-
/>
344-
</InputWrapper>
345-
</Field>
346-
<Field
347-
label='Private Key'
348-
helper={
349-
'The private key of the Agent, which is a Base64 encoded string.'
350-
}
351-
>
352-
<InputWrapper>
353-
<InputStyled
354-
disabled={agent !== undefined}
355-
type={showPrivateKey ? 'text' : 'password'}
356-
value={privateKey}
357-
onChange={e => {
358-
setPrivateKey(e.target.value);
359-
handleUpdateSubjectAndKey();
360-
}}
361-
/>
362-
<ButtonInput
363-
type='button'
364-
title={showPrivateKey ? 'Hide private key' : 'Show private key'}
365-
onClick={() => setShowPrivateKey(!showPrivateKey)}
366-
>
367-
{showPrivateKey ? <FaEyeSlash /> : <FaEye />}
368-
</ButtonInput>
369-
</InputWrapper>
370-
</Field>
371-
</React.Fragment>
372-
) : null}
373-
</form>
374-
);
375-
};

0 commit comments

Comments
 (0)