Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add onboarding flow for new users. #297

Merged
merged 49 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
6da018f
Try a setup wizard.
StevenDufresne Jul 15, 2024
64d38ef
More changes.
StevenDufresne Jul 16, 2024
a3fc16e
More changes
StevenDufresne Jul 17, 2024
53131c5
Fix the linter.
StevenDufresne Jul 17, 2024
7b4c749
Clean up
StevenDufresne Jul 17, 2024
7aaecf8
Update account-status to be 'home'.
StevenDufresne Jul 18, 2024
98335e8
Add back empty line.
StevenDufresne Jul 18, 2024
e347ab3
Update copy.
StevenDufresne Aug 8, 2024
3673f39
Add redirect logic.
StevenDufresne Aug 8, 2024
16ab35f
Fix spacing.
StevenDufresne Aug 9, 2024
a4152b5
Improve css.
StevenDufresne Aug 9, 2024
0db3f19
Font show if they have a primary.
StevenDufresne Aug 9, 2024
7d8b9e1
We can't check via the component because it reloads.
StevenDufresne Aug 9, 2024
70aa9c4
Remove eslint disable line.
StevenDufresne Aug 9, 2024
213d2d4
Remove empty line.
StevenDufresne Aug 9, 2024
8c1e2ea
Clean up formatting.
StevenDufresne Aug 11, 2024
e88b9c8
Revert "Clean up formatting."
StevenDufresne Aug 11, 2024
a13344b
clean up formatting.
StevenDufresne Aug 11, 2024
a494064
Lowercase setup radial.
StevenDufresne Aug 12, 2024
919094f
Trap the keyboard focus within the window.
StevenDufresne Aug 12, 2024
21213a9
Update the padding-top for the wizard.
StevenDufresne Aug 12, 2024
6d9b0b6
Update the component name.
StevenDufresne Aug 12, 2024
f6e3287
Update settings/src/components/first-time/first-time.js
StevenDufresne Aug 12, 2024
6a5aaaa
Prefer keys as the first option.
StevenDufresne Aug 12, 2024
e8ab11e
Move progress indicator about window.
StevenDufresne Aug 12, 2024
f4cd78b
Default to 2fa view.
StevenDufresne Aug 12, 2024
ea309fd
move the progress component.
StevenDufresne Aug 12, 2024
5268407
Update selected radial to be webauthn.
StevenDufresne Aug 12, 2024
4354dbe
Fix react error with map key.
StevenDufresne Aug 12, 2024
8becb71
Update radio list styles.
StevenDufresne Aug 12, 2024
23bf7b4
Update progress bar and titles.
StevenDufresne Aug 13, 2024
9d5b2a5
Move congratulations to its own component.
StevenDufresne Aug 13, 2024
8fe507f
Remove unsued button.
StevenDufresne Aug 13, 2024
8feef5d
Prepare for an inline view.
StevenDufresne Aug 13, 2024
7aec115
Update the inline experience.
StevenDufresne Aug 14, 2024
ffb5d73
Copy update.
StevenDufresne Aug 16, 2024
e10c09b
Add onboarding property to trigger the onboarding api.
StevenDufresne Aug 16, 2024
aab0063
Add onboarding state.
StevenDufresne Aug 16, 2024
ef1fe2f
Update how we deal with profile/security
StevenDufresne Aug 16, 2024
5c6519f
Remove empty reference.
StevenDufresne Aug 16, 2024
d65a1aa
Fix copy.
StevenDufresne Aug 16, 2024
6616652
Fix backup code initial setup.
StevenDufresne Aug 22, 2024
e46af35
Fix the redirecting for onboarding.
StevenDufresne Aug 22, 2024
b9ac9e3
Remove comment.
StevenDufresne Aug 22, 2024
88aed3e
Clean up spacing.
StevenDufresne Aug 22, 2024
2b0e567
Update the function name.
StevenDufresne Aug 23, 2024
774515e
Update the congratulation links.
StevenDufresne Aug 23, 2024
da1d148
Remove the empty line.
StevenDufresne Aug 23, 2024
b94a670
Rename onboarding to isonboarding.
StevenDufresne Aug 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions settings/src/components/backup-codes.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@

/**
* Setup and manage backup codes.
*
* @param props
* @param props.onSuccess
*/
export default function BackupCodes() {
export default function BackupCodes( { onSuccess = () => {} } ) {
const {
user: { backupCodesEnabled, hasPrimaryProvider, backupCodesRemaining },
backupCodesVerified,
Expand All @@ -32,10 +35,7 @@
<Notice status="error" isDismissible={ false }>
<Icon icon={ cancelCircleFilled } />
Please
<ScreenLink
screen="account-status"
anchorText="enable a Two-Factor security key or app"
/>
<ScreenLink screen="home" anchorText="enable a Two-Factor security key or app" />
before enabling backup codes.
</Notice>
);
Expand All @@ -47,7 +47,7 @@
regenerating ||
! backupCodesVerified
) {
return <Setup setRegenerating={ setRegenerating } />;
return <Setup setRegenerating={ setRegenerating } onSuccess={ onSuccess } />;
}

return <Manage setRegenerating={ setRegenerating } />;
Expand All @@ -58,8 +58,9 @@
*
* @param props
* @param props.setRegenerating
* @param props.onSuccess
*/
function Setup( { setRegenerating } ) {
function Setup( { setRegenerating, onSuccess } ) {
const {
setGlobalNotice,
user: { userRecord },
Expand Down Expand Up @@ -101,16 +102,17 @@
};

generateCodes();
}, [] );

Check warning on line 105 in settings/src/components/backup-codes.js

View workflow job for this annotation

GitHub Actions / All

React Hook useEffect has missing dependencies: 'setBackupCodesVerified', 'setError', and 'userRecord'. Either include them or remove the dependency array

// Finish the setup process.
const handleFinished = useCallback( async () => {

Check warning on line 108 in settings/src/components/backup-codes.js

View workflow job for this annotation

GitHub Actions / All

React Hook useCallback does nothing when called with only one argument. Did you forget to pass an array of dependencies?
// TODO: Add try catch here after https://github.com/WordPress/wporg-two-factor/pull/187/files is merged.
// The codes have already been saved to usermeta, see `generateCodes()` above.
setBackupCodesVerified( true );
await refreshRecord( userRecord ); // This has the intended side-effect of redirecting to the Manage screen.
setGlobalNotice( 'Backup codes have been enabled.' );
setRegenerating( false );
onSuccess();
} );

return (
Expand Down
6 changes: 6 additions & 0 deletions settings/src/components/backup-codes.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
margin: 0;
}

@media (max-width: 600px) {
ol {
column-count: 1;
}
}

li::marker {
/* todo: a11y issues w/ contrast here? mockup calls for this to be lighter than the backup code
-- presumably so that users don't mistakenly think it's part of the code --
Expand Down
95 changes: 95 additions & 0 deletions settings/src/components/first-time/congratulations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* WordPress dependencies
*/
import { Button } from '@wordpress/components';
import { useContext } from '@wordpress/element';

/**
* Internal dependencies
*/
import { GlobalContext } from '../../script';

/**
* Check if the URL is valid. Make sure it stays on wordpress.org.
*
* @param url
* @return {boolean} Whether it's a valid URL.
*/
const isValidUrl = ( url ) => {
try {
const { hostname } = new URL( url );
return hostname.endsWith( 'wordpress.org' );
} catch ( exception ) {
return false;
}
};

export default function Congratulations() {
const {
user: { webAuthnEnabled, totpEnabled },
} = useContext( GlobalContext );

const getAuthenticationMethod = () => {
if ( webAuthnEnabled && totpEnabled ) {
return null;
}

return (
<ul>
<li>
{ totpEnabled && (
<a href="https://profiles.wordpress.org/me/profile/edit/group/3/?screen=webauthn">
Set up a security key
</a>
) }

{ webAuthnEnabled && (
<a href="https://profiles.wordpress.org/me/profile/edit/group/3/?screen=totp">
Set up one time password
</a>
) }
</li>
</ul>
);
};

return (
<>
<p>
To ensure the highest level of security for your account, please remember to keep
your authentication methods up-to-date, and consult{ ' ' }
<a href="//make.wordpress.org/meta/handbook/tutorials-guides/configuring-two-factor-authentication/">
our documentation
</a>{ ' ' }
if you need help or have any questions.
</p>
<p>
We recommend configuring multiple authentication methods to guarantee you always
have access to your account.
</p>

{ getAuthenticationMethod() }

<div className="wporg-2fa__submit-actions">
<Button
onClick={ () => {
const redirectTo = new URLSearchParams( window.location.search ).get(
'redirect_to'
);

if ( redirectTo && isValidUrl( redirectTo ) ) {
window.location.href = redirectTo;
} else {
window.location.href =
'//profiles.wordpress.org/me/profile/edit/group/3';
}
} }
isPrimary
>
Continue
</Button>
<Button variant="link">View security settings</Button>
</div>
</>
);
}
110 changes: 110 additions & 0 deletions settings/src/components/first-time/first-time.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/**
* WordPress dependencies
*/
import { useContext, useRef } from '@wordpress/element';

Check failure on line 4 in settings/src/components/first-time/first-time.js

View workflow job for this annotation

GitHub Actions / All

'useRef' is defined but never used

/**
* Internal dependencies
*/
import ScreenNavigation from '../screen-navigation';
import TOTP from '../totp';
import WebAuthn from '../webauthn/webauthn';
import BackupCodes from '../backup-codes';
import SetupProgressBar from './setup-progress-bar';
import Home from './home';
import Congratulations from './congratulations';
import { GlobalContext } from '../../script';

/**
* Render the correct component based on the URL.
*/
export default function FirstTime() {
const { navigateToScreen, screen } = useContext( GlobalContext );

// The index is the URL slug and the value is the React component.
const screens = {
home: {
stepIndex: 0,
title: 'Set up two-factor authentication',
component: (
<Home
onSelect={ ( val ) => {
navigateToScreen( val );
} }
/>
),
},
totp: {
stepIndex: 1,
title: 'Set up one-time password',
component: (
<TOTP
onSuccess={ () => {
navigateToScreen( 'backup-codes' );
} }
/>
),
},
webauthn: {
stepIndex: 1,
title: 'Set up security key',
component: (
<WebAuthn
onKeyAdd={ () => {
navigateToScreen( 'backup-codes' );
} }
/>
),
},
'backup-codes': {
stepIndex: 2,
title: 'Print backup codes',
component: (
<BackupCodes
onSuccess={ () => {
navigateToScreen( 'congratulations' );
} }
/>
),
},
congratulations: {
stepIndex: 3,
component: <Congratulations />,
},
};

const currentStepIndex = screens[ screen ].stepIndex;
let currentScreenComponent = null;

if ( 'congratulations' === screen ) {
currentScreenComponent = (
<ScreenNavigation screen={ screen } title={ 'Setup complete' } canNavigate={ false }>
{ screens[ screen ].component }
</ScreenNavigation>
);
} else {
currentScreenComponent = (
<>
<SetupProgressBar
currentStepIndex={ currentStepIndex }
steps={ [ 'Select', 'Configure', 'Print' ] }
/>
<ScreenNavigation
screen={ screen }
title={ screens[ screen ].title }
canNavigate={ currentStepIndex !== 0 }
>
{ screens[ screen ].component }
</ScreenNavigation>
</>
);
}

return (
<div className="wporg-2fa__first-time">
<div className={ `wporg-2fa__first-time__inner-content ${ screen }` }>
{ currentScreenComponent }
</div>
</div>
);
}
75 changes: 75 additions & 0 deletions settings/src/components/first-time/first-time.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
.wporg-2fa__first-time {
padding: 0 16px;
min-height: 600px;
display: flex;
justify-content: center;

.wporg-2fa__first-time__inner-content {
padding: 0 0 100px;
display: flex;
flex-direction: column;
align-items: center;

@media (min-width: 600px) {
width: 600px;
}

// Preven cards from shrinking.
.components-card {
width: 100%;
}

.components-card__body {
padding: 32px;
padding-top: 16px;
}
}

.wporg-2fa__first-time__inner-content.congratulations {
justify-content: center;
}

.wporg-2fa__first-time-default {
display: flex;
flex-direction: column;
gap: 18px;
padding-top: 8px;
}

.wporg-2fa__first-time-default-item {
margin: 0;
position: relative;
display: flex;
gap: 10px;
align-items: flex-start;
cursor: pointer;

span {
font-weight: 600;
z-index: 1;
}

input {
position: relative;
z-index: 1;
margin-top: 4px;
margin-left: 6px;
}

> div {
display: flex;
flex-direction: column;
align-items: flex-start;

p {
color: var(--wp--preset--color--charcoal-4, #656a71);
margin: 0;
z-index: 1;
}
}
}
}

.wporg-2fa__congratulations h3 {
margin-bottom: 14px;
}
Loading
Loading