Skip to content

Commit

Permalink
Various registration UX improvements #238
Browse files Browse the repository at this point in the history
  • Loading branch information
joepio committed Feb 27, 2023
1 parent b4a2b18 commit 302b79e
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 36 deletions.
5 changes: 3 additions & 2 deletions data-browser/src/components/RegisterSignIn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ function Register({ close }) {
<InputWrapper>
<InputStyled
autoFocus={true}
// pattern={emailRegex}
type={'email'}
required
value={email}
Expand Down Expand Up @@ -186,8 +185,10 @@ function SignIn() {
</DialogTitle>
<DialogContent>
<SettingsAgent />
<p>Lost your passphrase?</p>
</DialogContent>
<DialogActions>
<p>Lost your passphrase?</p>
</DialogActions>
</>
);
}
1 change: 1 addition & 0 deletions data-browser/src/helpers/loggingHandlers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import BugsnagPluginReact, {
BugsnagErrorBoundary,
} from '@bugsnag/plugin-react';
import React from 'react';
import { toast } from 'react-hot-toast';
import { isDev } from '../config';

export function handleError(e: Error): void {
Expand Down
79 changes: 57 additions & 22 deletions data-browser/src/routes/ConfirmEmail.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { confirmEmail, useStore } from '@tomic/react';
import * as React from 'react';
import { useState } from 'react';
import { CodeBlock } from '../components/CodeBlock';
import toast from 'react-hot-toast';
import { Button } from '../components/Button';
import { CodeBlockStyled } from '../components/CodeBlock';
import { ContainerNarrow } from '../components/Containers';
import { isDev } from '../config';
import { useSettings } from '../helpers/AppSettings';
import { handleError } from '../helpers/handlers';
import {
useCurrentSubject,
useSubjectParam,
Expand All @@ -19,10 +20,13 @@ const ConfirmEmail: React.FunctionComponent = () => {
const [secret, setSecret] = useState('');
const store = useStore();
const [token] = useSubjectParam('token');
const { agent, setAgent } = useSettings();
const { setAgent } = useSettings();
const [destinationToGo, setDestination] = useState<string>();
const [err, setErr] = useState<Error | undefined>(undefined);
const [triedConfirm, setTriedConfirm] = useState(false);

const handleConfirm = async () => {
const handleConfirm = React.useCallback(async () => {
setTriedConfirm(true);
let tokenUrl = subject as string;

if (isDev()) {
Expand All @@ -37,37 +41,68 @@ const ConfirmEmail: React.FunctionComponent = () => {
store,
tokenUrl,
);
setAgent(newAgent);
setSecret(newAgent.buildSecret());
setDestination(destination);
setAgent(newAgent);
toast.success('Email confirmed!');
} catch (e) {
handleError(e);
setErr(e);
}
}, [subject]);

if (!triedConfirm && subject) {
handleConfirm();
}

if (err) {
if (err.message.includes('expired')) {
return (
<ContainerNarrow>
The link has expired. Request a new one by Registering again.
</ContainerNarrow>
);
}
};

if (!agent) {
return (
<ContainerNarrow>
<button onClick={handleConfirm}>confirm</button>
</ContainerNarrow>
);
return <ContainerNarrow>{err?.message}</ContainerNarrow>;
}

if (secret) {
return <SavePassphrase secret={secret} destination={destinationToGo} />;
}

return <ContainerNarrow>Verifying token...</ContainerNarrow>;
};

function SavePassphrase({ secret, destination }) {
const [copied, setCopied] = useState(false);

function copyToClipboard() {
setCopied(secret);
navigator.clipboard.writeText(secret || '');
toast.success('Copied to clipboard');
}

return (
<ContainerNarrow>
<h1>Save your Passphrase</h1>
<h1>Mail confirmed, please save your passphrase</h1>
<p>
Your Passphrase is like your password. Never share it with anyone. Use a
password manager to store it securely. You will need this to log in
next!
password manager like{' '}
<a href='https://bitwarden.com/' target='_blank' rel='noreferrer'>
BitWarden
</a>{' '}
to store it securely.
</p>
<CodeBlock content={secret} wrapContent />
{/* <Button onClick={handleGoToDestination}>Continue here</Button> */}
<a href={destinationToGo} target='_blank' rel='noreferrer'>
Open my new Drive!
</a>
<CodeBlockStyled wrapContent>{secret}</CodeBlockStyled>
{copied ? (
<a href={destination} target='_blank' rel='noreferrer'>
{"I've saved my PassPhrase, open my new Drive!"}
</a>
) : (
<Button onClick={copyToClipboard}>Copy Passphrase</Button>
)}
</ContainerNarrow>
);
};
}

export default ConfirmEmail;
3 changes: 3 additions & 0 deletions data-browser/src/views/ErrorPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ function ErrorPage({ resource }: ResourcePageProps): JSX.Element {
}, [agent]);

if (isUnauthorized(resource.error)) {
// This might be a bit too aggressive, but it fixes 'Unauthorized' messages after signing in to a new drive.
store.fetchResource(subject);

return (
<ContainerWide>
<Column>
Expand Down
29 changes: 17 additions & 12 deletions lib/src/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ export async function register(
* If there is no agent in the store, a new one will be created. */
export async function confirmEmail(
store: Store,
/** Full http URL including the `token` query parameter */
tokenURL: string,
): Promise<{ agent: Agent; destination: string }> {
const url = new URL(tokenURL);
Expand Down Expand Up @@ -204,17 +205,21 @@ export const removeCookieAuthentication = () => {
};

function parseJwt(token) {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(
window
.atob(base64)
.split('')
.map(function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
})
.join(''),
);
try {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(
window
.atob(base64)
.split('')
.map(function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
})
.join(''),
);

return JSON.parse(jsonPayload);
return JSON.parse(jsonPayload);
} catch (e) {
throw new Error('Invalid token: ' + e);
}
}

0 comments on commit 302b79e

Please sign in to comment.