Skip to content

Commit a30d3e2

Browse files
committed
Add-pubkey modal, refactor register comps #238
1 parent 5b19b63 commit a30d3e2

File tree

4 files changed

+189
-50
lines changed

4 files changed

+189
-50
lines changed

data-browser/src/components/Dialog/useDialog.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ export const useDialog = (): UseDialogReturnType => {
2323
}, []);
2424

2525
const close = useCallback(() => {
26-
console.log('close', close);
2726
setShowDialog(false);
2827
}, []);
2928

data-browser/src/components/RegisterSignIn.tsx

+148-48
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ import {
88
import React, { FormEvent, useCallback, useEffect, useState } from 'react';
99
import { useSettings } from '../helpers/AppSettings';
1010
import { Button } from './Button';
11-
import { nameRegex, register, useServerURL, useStore } from '@tomic/react';
11+
import {
12+
addPublicKey,
13+
nameRegex,
14+
register as createRegistration,
15+
useServerURL,
16+
useStore,
17+
} from '@tomic/react';
1218
import Field from './forms/Field';
1319
import { InputWrapper, InputStyled } from './forms/InputStyles';
1420
import { Row } from './Row';
@@ -20,6 +26,16 @@ interface RegisterSignInProps {
2026
redirect?: string;
2127
}
2228

29+
/** What is currently showing */
30+
enum PageStateOpts {
31+
none,
32+
signIn,
33+
register,
34+
reset,
35+
mailSentRegistration,
36+
mailSentAddPubkey,
37+
}
38+
2339
/**
2440
* Two buttons: Register / Sign in.
2541
* Opens a Dialog / Modal with the appropriate form.
@@ -29,7 +45,8 @@ export function RegisterSignIn({
2945
}: React.PropsWithChildren<RegisterSignInProps>): JSX.Element {
3046
const { dialogProps, show, close } = useDialog();
3147
const { agent } = useSettings();
32-
const [isRegistering, setRegister] = useState(true);
48+
const [pageState, setPageState] = useState<PageStateOpts>(PageStateOpts.none);
49+
const [email, setEmail] = useState('');
3350

3451
if (agent) {
3552
return <>{children}</>;
@@ -39,7 +56,7 @@ export function RegisterSignIn({
3956
<Row>
4057
<Button
4158
onClick={() => {
42-
setRegister(true);
59+
setPageState(PageStateOpts.register);
4360
show();
4461
}}
4562
>
@@ -48,27 +65,116 @@ export function RegisterSignIn({
4865
<Button
4966
subtle
5067
onClick={() => {
51-
setRegister(false);
68+
setPageState(PageStateOpts.signIn);
5269
show();
5370
}}
5471
>
5572
Sign In
5673
</Button>
5774
</Row>
5875
<Dialog {...dialogProps}>
59-
{isRegistering ? <Register close={close} /> : <SignIn />}
76+
{pageState === PageStateOpts.register && (
77+
<Register
78+
setPageState={setPageState}
79+
email={email}
80+
setEmail={setEmail}
81+
/>
82+
)}
83+
{pageState === PageStateOpts.signIn && (
84+
<SignIn setPageState={setPageState} />
85+
)}
86+
{pageState === PageStateOpts.reset && (
87+
<Reset
88+
email={email}
89+
setEmail={setEmail}
90+
setPageState={setPageState}
91+
/>
92+
)}
93+
{pageState === PageStateOpts.mailSentRegistration && (
94+
<MailSentConfirm
95+
email={email}
96+
close={close}
97+
message={'Your account will be created when you open that link.'}
98+
/>
99+
)}
100+
{pageState === PageStateOpts.mailSentAddPubkey && (
101+
<MailSentConfirm
102+
email={email}
103+
close={close}
104+
message={'Click that link to create a new PassPhrase.'}
105+
/>
106+
)}
60107
</Dialog>
61108
</>
62109
);
63110
}
64111

65-
function Register({ close }) {
112+
function Reset({ email, setEmail, setPageState }) {
113+
const store = useStore();
114+
const [err, setErr] = useState<Error | undefined>(undefined);
115+
116+
const handleRequestReset = useCallback(async () => {
117+
try {
118+
await addPublicKey(store, email);
119+
setPageState(PageStateOpts.mailSentAddPubkey);
120+
} catch (e) {
121+
setErr(e);
122+
}
123+
}, [email]);
124+
125+
return (
126+
<>
127+
<DialogTitle>
128+
<h1>Reset your PassKey</h1>
129+
</DialogTitle>
130+
<DialogContent>
131+
<p>
132+
{
133+
"Lost it? No worries, we'll send a link that let's you create a new one."
134+
}
135+
</p>
136+
<EmailField
137+
email={email}
138+
setEmail={(e: any) => {
139+
setErr(undefined);
140+
setEmail(e);
141+
}}
142+
/>
143+
{err && <ErrorLook>{err.message}</ErrorLook>}
144+
</DialogContent>
145+
<DialogActions>
146+
<Button onClick={handleRequestReset}>Send me</Button>
147+
</DialogActions>
148+
</>
149+
);
150+
}
151+
152+
function MailSentConfirm({ email, close, message }) {
153+
return (
154+
<>
155+
<DialogTitle>
156+
<h1>Go to your email inbox</h1>
157+
</DialogTitle>
158+
<DialogContent>
159+
<p>
160+
{"We've sent a confirmation link to "}
161+
<strong>{email}</strong>
162+
{'.'}
163+
</p>
164+
<p>{message}</p>
165+
</DialogContent>
166+
<DialogActions>
167+
<Button onClick={close}>{"Ok, I'll open my mailbox!"}</Button>
168+
</DialogActions>
169+
</>
170+
);
171+
}
172+
173+
function Register({ setPageState, email, setEmail }) {
66174
const [name, setName] = useState('');
67-
const [email, setEmail] = useState('');
68175
const [serverUrlStr] = useServerURL();
69176
const [nameErr, setErr] = useState<Error | undefined>(undefined);
70177
const store = useStore();
71-
const [mailSent, setMailSent] = useState(false);
72178

73179
const serverUrl = new URL(serverUrlStr);
74180
serverUrl.host = `${name}.${serverUrl.host}`;
@@ -93,36 +199,15 @@ function Register({ close }) {
93199
}
94200

95201
try {
96-
await register(store, name, email);
97-
setMailSent(true);
202+
await createRegistration(store, name, email);
203+
setPageState(PageStateOpts.mailSentRegistration);
98204
} catch (er) {
99205
setErr(er);
100206
}
101207
},
102208
[name, email],
103209
);
104210

105-
if (mailSent) {
106-
return (
107-
<>
108-
<DialogTitle>
109-
<h1>Go to your email inbox</h1>
110-
</DialogTitle>
111-
<DialogContent>
112-
<p>
113-
{"We've sent a confirmation link to "}
114-
<strong>{email}</strong>
115-
{'.'}
116-
</p>
117-
<p>Your account will be created when you open that link.</p>
118-
</DialogContent>
119-
<DialogActions>
120-
<Button onClick={close}>Ok, I will!</Button>
121-
</DialogActions>
122-
</>
123-
);
124-
}
125-
126211
return (
127212
<>
128213
<DialogTitle>
@@ -147,48 +232,63 @@ function Register({ close }) {
147232
/>
148233
</InputWrapper>
149234
</Field>
150-
<Field label='E-mail'>
151-
<InputWrapper>
152-
<InputStyled
153-
autoFocus={true}
154-
type={'email'}
155-
required
156-
value={email}
157-
onChange={e => {
158-
setEmail(e.target.value);
159-
}}
160-
/>
161-
</InputWrapper>
162-
</Field>
235+
<EmailField email={email} setEmail={setEmail} />
163236
{name && nameErr && <ErrorLook>{nameErr.message}</ErrorLook>}
164237
</form>
165238
</DialogContent>
166239
<DialogActions>
240+
<Button subtle onClick={() => setPageState(PageStateOpts.signIn)}>
241+
Sign in
242+
</Button>
167243
<Button
168244
type='submit'
169245
form='register-form'
170246
disabled={!name || !!nameErr}
171247
onClick={handleSubmit}
172248
>
173-
Create
249+
Save
174250
</Button>
175251
</DialogActions>
176252
</>
177253
);
178254
}
179255

180-
function SignIn() {
256+
function SignIn({ setPageState }) {
181257
return (
182258
<>
183259
<DialogTitle>
184-
<h1>Sign in </h1>
260+
<h1>Sign in</h1>
185261
</DialogTitle>
186262
<DialogContent>
187263
<SettingsAgent />
188264
</DialogContent>
189265
<DialogActions>
190-
<p>Lost your passphrase?</p>
266+
<Button subtle onClick={() => setPageState(PageStateOpts.register)}>
267+
Register
268+
</Button>
269+
<Button subtle onClick={() => setPageState(PageStateOpts.reset)}>
270+
I lost my passphrase
271+
</Button>
191272
</DialogActions>
192273
</>
193274
);
194275
}
276+
277+
function EmailField({ setEmail, email }) {
278+
return (
279+
<Field label='E-mail'>
280+
<InputWrapper>
281+
<InputStyled
282+
// This is not properly working atm
283+
autoFocus
284+
type={'email'}
285+
required
286+
value={email}
287+
onChange={e => {
288+
setEmail(e.target.value);
289+
}}
290+
/>
291+
</InputWrapper>
292+
</Field>
293+
);
294+
}

data-browser/src/views/ErrorPage.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ function ErrorPage({ resource }: ResourcePageProps): JSX.Element {
3333
<h1>Unauthorized</h1>
3434
{agent ? (
3535
<>
36+
<p>
37+
{
38+
"You don't have access to this. Try asking for access, or sign in with a different account."
39+
}
40+
</p>
3641
<ErrorBlock error={resource.error!} />
3742
<span>
3843
<Button onClick={() => store.fetchResourceFromServer(subject)}>

lib/src/authentication.ts

+36-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,34 @@ export async function register(
141141
const description = resource.get(properties.description) as string;
142142

143143
if (!description.includes('success')) {
144-
throw new Error('ERRORORRRR');
144+
throw new Error('Expected a `success` message, did not receive one');
145+
}
146+
147+
return;
148+
}
149+
150+
/** Asks the server to add a public key to an account. Will lead to a confirmation link being sent */
151+
export async function addPublicKey(store: Store, email: string): Promise<void> {
152+
if (!email) {
153+
throw new Error('No email provided');
154+
}
155+
156+
const url = new URL('/add-public-key', store.getServerUrl());
157+
url.searchParams.set('email', email);
158+
const resource = await store.getResourceAsync(url.toString());
159+
160+
if (!resource) {
161+
throw new Error('No resource received');
162+
}
163+
164+
if (resource.error) {
165+
throw resource.error;
166+
}
167+
168+
const description = resource.get(properties.description) as string;
169+
170+
if (!description.includes('success')) {
171+
throw new Error('Expected a `success` message, did not receive one');
145172
}
146173

147174
return;
@@ -169,13 +196,21 @@ export async function confirmEmail(
169196

170197
let agent = store.getAgent();
171198

199+
// No agent, create a new one
172200
if (!agent) {
173201
const keypair = await generateKeyPair();
174202
const newAgent = new Agent(keypair.privateKey);
175203
newAgent.subject = `${store.getServerUrl()}/agents/${parsed.name}`;
176204
agent = newAgent;
177205
}
178206

207+
// An agent already exists, make sure it matches the confirm email token
208+
if (!agent?.subject?.includes(parsed.name)) {
209+
throw new Error(
210+
'You cannot confirm this email, you are already logged in as a different user',
211+
);
212+
}
213+
179214
url.searchParams.set('public-key', await agent.getPublicKey());
180215
const resource = await store.getResourceAsync(url.toString());
181216

0 commit comments

Comments
 (0)