Skip to content

Commit ebfd3f2

Browse files
committed
Confirm Email
1 parent 5711e37 commit ebfd3f2

File tree

10 files changed

+220
-67
lines changed

10 files changed

+220
-67
lines changed

.vscode/tasks.json

+11
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@
1212
"isBackground": true,
1313
"group": "build"
1414
},
15+
{
16+
"type": "npm",
17+
"script": "build-server",
18+
"problemMatcher": [
19+
"$tsc-watch"
20+
],
21+
"label": "build server JS assets",
22+
"detail": "pnpm workspace @tomic/data-browser build-server",
23+
"isBackground": true,
24+
"group": "build"
25+
},
1526
{
1627
"type": "npm",
1728
"script": "test",

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

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

2525
const close = useCallback(() => {
26+
console.log('close', close);
2627
setShowDialog(false);
2728
}, []);
2829

data-browser/src/components/ErrorLook.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ export function ErrorBlock({ error, showTrace }: ErrorBlockProps): JSX.Element {
2626
{showTrace && (
2727
<>
2828
Stack trace:
29-
<CodeBlock>{error.stack}</CodeBlock>
29+
<CodeBlock
30+
style={{
31+
maxHeight: '10rem',
32+
}}
33+
>
34+
{error.stack}
35+
</CodeBlock>
3036
</>
3137
)}
3238
</Column>

data-browser/src/components/RegisterSignIn.tsx

+15-39
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,11 @@ import {
88
import React, { FormEvent, useCallback, useEffect, useState } from 'react';
99
import { useSettings } from '../helpers/AppSettings';
1010
import { Button } from './Button';
11-
import {
12-
Agent,
13-
nameRegex,
14-
register,
15-
useServerURL,
16-
useStore,
17-
} from '@tomic/react';
11+
import { nameRegex, register, useServerURL, useStore } from '@tomic/react';
1812
import Field from './forms/Field';
1913
import { InputWrapper, InputStyled } from './forms/InputStyles';
2014
import { Row } from './Row';
2115
import { ErrorLook } from './ErrorLook';
22-
import { CodeBlock } from './CodeBlock';
2316
import { SettingsAgent } from './SettingsAgent';
2417

2518
interface RegisterSignInProps {
@@ -34,9 +27,9 @@ interface RegisterSignInProps {
3427
export function RegisterSignIn({
3528
children,
3629
}: React.PropsWithChildren<RegisterSignInProps>): JSX.Element {
37-
const { dialogProps, show } = useDialog();
30+
const { dialogProps, show, close } = useDialog();
3831
const { agent } = useSettings();
39-
const [isRegister, setRegister] = useState(true);
32+
const [isRegistering, setRegister] = useState(true);
4033

4134
if (agent) {
4235
return <>{children}</>;
@@ -63,23 +56,19 @@ export function RegisterSignIn({
6356
</Button>
6457
</Row>
6558
<Dialog {...dialogProps}>
66-
{isRegister ? <Register /> : <SignIn />}
59+
{isRegistering ? <Register close={close} /> : <SignIn />}
6760
</Dialog>
6861
</>
6962
);
7063
}
7164

72-
function Register() {
65+
function Register({ close }) {
7366
const [name, setName] = useState('');
7467
const [email, setEmail] = useState('');
75-
const [secret, setSecret] = useState('');
76-
const [driveURL, setDriveURL] = useState('');
77-
const [newAgent, setNewAgent] = useState<Agent | undefined>(undefined);
7868
const [serverUrlStr] = useServerURL();
7969
const [nameErr, setErr] = useState<Error | undefined>(undefined);
80-
const doRegister = useCallback(register, []);
81-
const { setAgent } = useSettings();
8270
const store = useStore();
71+
const [mailSent, setMailSent] = useState(false);
8372

8473
const serverUrl = new URL(serverUrlStr);
8574
serverUrl.host = `${name}.${serverUrl.host}`;
@@ -104,44 +93,31 @@ function Register() {
10493
}
10594

10695
try {
107-
const { driveURL: newDriveURL, agent } = await doRegister(
108-
store,
109-
name,
110-
email,
111-
);
112-
setDriveURL(newDriveURL);
113-
setSecret(agent.buildSecret());
114-
setNewAgent(agent);
96+
await register(store, name, email);
97+
setMailSent(true);
11598
} catch (er) {
11699
setErr(er);
117100
}
118101
},
119102
[name, email],
120103
);
121104

122-
const handleSaveAgent = useCallback(() => {
123-
setAgent(newAgent);
124-
}, [newAgent]);
125-
126-
if (driveURL) {
105+
if (mailSent) {
127106
return (
128107
<>
129108
<DialogTitle>
130-
<h1>Save your Passphrase, {name}</h1>
109+
<h1>Go to your email inbox</h1>
131110
</DialogTitle>
132111
<DialogContent>
133112
<p>
134-
Your Passphrase is like your password. Never share it with anyone.
135-
Use a password manager to store it securely. You will need this to
136-
log in next!
113+
{"We've sent a confirmation link to "}
114+
<strong>{email}</strong>
115+
{'.'}
137116
</p>
138-
<CodeBlock content={secret} wrapContent />
117+
<p>Your account will be created when you open that link.</p>
139118
</DialogContent>
140119
<DialogActions>
141-
<Button onClick={handleSaveAgent}>Continue here</Button>
142-
<a href={driveURL} target='_blank' rel='noreferrer'>
143-
Open my new Drive!
144-
</a>
120+
<Button onClick={close}>Ok, I will!</Button>
145121
</DialogActions>
146122
</>
147123
);
+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { confirmEmail, useStore } from '@tomic/react';
2+
import * as React from 'react';
3+
import { useState } from 'react';
4+
import { CodeBlock } from '../components/CodeBlock';
5+
import { ContainerNarrow } from '../components/Containers';
6+
import { isDev } from '../config';
7+
import { useSettings } from '../helpers/AppSettings';
8+
import { handleError } from '../helpers/handlers';
9+
import {
10+
useCurrentSubject,
11+
useSubjectParam,
12+
} from '../helpers/useCurrentSubject';
13+
import { paths } from './paths';
14+
15+
/** Route that connects to `/confirm-email`, which confirms an email and creates a secret key. */
16+
const ConfirmEmail: React.FunctionComponent = () => {
17+
// Value shown in navbar, after Submitting
18+
const [subject] = useCurrentSubject();
19+
const [secret, setSecret] = useState('');
20+
const store = useStore();
21+
const [token] = useSubjectParam('token');
22+
const { agent, setAgent } = useSettings();
23+
const [destinationToGo, setDestination] = useState<string>();
24+
25+
const handleConfirm = async () => {
26+
let tokenUrl = subject as string;
27+
28+
if (isDev()) {
29+
const url = new URL(store.getServerUrl());
30+
url.pathname = paths.confirmEmail;
31+
url.searchParams.set('token', token as string);
32+
tokenUrl = url.href;
33+
}
34+
35+
try {
36+
const { agent: newAgent, destination } = await confirmEmail(
37+
store,
38+
tokenUrl,
39+
);
40+
setAgent(newAgent);
41+
setSecret(newAgent.buildSecret());
42+
setDestination(destination);
43+
} catch (e) {
44+
handleError(e);
45+
}
46+
};
47+
48+
if (!agent) {
49+
return (
50+
<ContainerNarrow>
51+
<button onClick={handleConfirm}>confirm</button>
52+
</ContainerNarrow>
53+
);
54+
}
55+
56+
return (
57+
<ContainerNarrow>
58+
<h1>Save your Passphrase</h1>
59+
<p>
60+
Your Passphrase is like your password. Never share it with anyone. Use a
61+
password manager to store it securely. You will need this to log in
62+
next!
63+
</p>
64+
<CodeBlock content={secret} wrapContent />
65+
{/* <Button onClick={handleGoToDestination}>Continue here</Button> */}
66+
<a href={destinationToGo} target='_blank' rel='noreferrer'>
67+
Open my new Drive!
68+
</a>
69+
</ContainerNarrow>
70+
);
71+
};
72+
73+
export default ConfirmEmail;

data-browser/src/routes/Routes.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { paths } from './paths';
1717
import ResourcePage from '../views/ResourcePage';
1818
import { ShareRoute } from './ShareRoute';
1919
import { Sandbox } from './Sandbox';
20+
import ConfirmEmail from './ConfirmEmail';
2021

2122
/** Server URLs should have a `/` at the end */
2223
const homeURL = window.location.origin + '/';
@@ -44,6 +45,7 @@ export function AppRoutes(): JSX.Element {
4445
<Route path={paths.about} element={<About />} />
4546
<Route path={paths.search} element={<Search />} />
4647
{isDev && <Route path={paths.sandbox} element={<Sandbox />} />}
48+
<Route path={paths.confirmEmail} element={<ConfirmEmail />} />
4749
<Route path='/' element={<ResourcePage subject={homeURL} />} />
4850
<Route path='*' element={<Local />} />
4951
</Routes>

data-browser/src/routes/paths.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ export const paths = {
1212
about: '/app/about',
1313
allVersions: '/all-versions',
1414
sandbox: '/sandbox',
15+
confirmEmail: '/confirm-email',
1516
fetchBookmark: '/fetch-bookmark',
1617
};

data-browser/src/views/CrashPage.tsx

+28-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,32 @@ type ErrorPageProps = {
1414
clearError: () => void;
1515
};
1616

17+
const githubIssueTemplate = (
18+
message,
19+
stack,
20+
) => `**Describe what you did to produce the bug**
21+
22+
## Error message
23+
\`\`\`
24+
${message}
25+
\`\`\`
26+
27+
## Stack trace
28+
\`\`\`
29+
${stack}
30+
\`\`\`
31+
`;
32+
33+
function createGithubIssueLink(error: Error): string {
34+
const url = new URL(
35+
'https://github.com/atomicdata-dev/atomic-data-browser/issues/new',
36+
);
37+
url.searchParams.set('body', githubIssueTemplate(error.message, error.stack));
38+
url.searchParams.set('labels', 'bug');
39+
40+
return url.href;
41+
}
42+
1743
/** If the entire app crashes, show this page */
1844
function CrashPage({
1945
resource,
@@ -26,6 +52,7 @@ function CrashPage({
2652
<Column>
2753
{children ? children : <ErrorBlock error={error} showTrace />}
2854
<Row>
55+
<a href={createGithubIssueLink(error)}>Create Github issue</a>
2956
{clearError && <Button onClick={clearError}>Clear error</Button>}
3057
<Button
3158
onClick={() =>
@@ -35,7 +62,7 @@ function CrashPage({
3562
)
3663
}
3764
>
38-
Try Again
65+
Refresh page
3966
</Button>
4067
</Row>
4168
</Column>

0 commit comments

Comments
 (0)