Skip to content

Commit

Permalink
Update animations and modals (#230)
Browse files Browse the repository at this point in the history
* Adjust success animations

* Hide modal actions while action is processing

* Use Flex component for Success layout

* Ignore key name field for 1Password

* Avoid classname clash with account-status icons

* Add confirmation modal for disabling TOTP

* Clean up destructuring

* Stop confirmation modal size change when processing

* Consistent handling of loading and error states in flows

* Fix confirmation modal comments
  • Loading branch information
adamwoodnz authored Aug 28, 2023
1 parent 865eb9d commit ea6a8c8
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 132 deletions.
15 changes: 8 additions & 7 deletions settings/src/components/success.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* WordPress dependencies
*/
import { Flex } from '@wordpress/components';
import { useState } from '@wordpress/element';
import { Icon, check } from '@wordpress/icons';

Expand All @@ -18,19 +19,19 @@ export default function Success( { message, afterTimeout } ) {

if ( ! hasTimer ) {
// Time matches the length of the CSS animation property on .wporg-2fa__success
setTimeout( afterTimeout, 4000 );
setTimeout( afterTimeout, 5000 );
setHasTimer( true );
}

return (
<>
<p className="wporg-2fa__screen-intro">{ message }</p>
<Flex className="wporg-2fa__success" direction="column">
<p>{ message }</p>

<p className="wporg-2fa__webauthn-register-key-status" aria-hidden>
<div className="wporg-2fa__success">
<div className="wporg-2fa__process-status" aria-hidden>
<div className="wporg-2fa__success-animation">
<Icon icon={ check } />
</div>
</p>
</>
</div>
</Flex>
);
}
13 changes: 10 additions & 3 deletions settings/src/components/success.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
.wporg-2fa__success {
flex: 1;
}

@keyframes success {
0% {
transform: scale(0);
}
10% {
transform: scale(1.5);
transform: scale(0);
}
20% {
transform: scale(1.5);
}
30% {
transform: scale(1);
}
80% {
Expand All @@ -16,7 +23,7 @@
}
}

.wporg-2fa__success {
.wporg-2fa__success-animation {
transform: scale(0);
background: #33F078;
border-radius: 50%;
Expand All @@ -25,5 +32,5 @@
justify-content: center;
width: 32px;
height: 32px;
animation: success 4s ease-in-out;
animation: success 5s ease-in-out;
}
169 changes: 131 additions & 38 deletions settings/src/components/totp.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* WordPress dependencies
*/
import apiFetch from '@wordpress/api-fetch';
import { Button, Notice, Flex } from '@wordpress/components';
import { Button, Modal, Notice, Flex, Spinner } from '@wordpress/components';
import { Icon, cancelCircleFilled } from '@wordpress/icons';
import { RawHTML, useCallback, useContext, useEffect, useRef, useState } from '@wordpress/element';

Expand All @@ -29,10 +29,12 @@ export default function TOTP() {

if ( success ) {
return (
<Success
message="Success! Your two-factor authentication app is set up."
afterTimeout={ afterTimeout }
/>
<Flex className="wporg-2fa__totp_success" direction="column">
<Success
message="Success! Your two-factor authentication app is set up."
afterTimeout={ afterTimeout }
/>
</Flex>
);
}

Expand Down Expand Up @@ -61,6 +63,7 @@ function Setup( { setSuccess } ) {
const [ error, setError ] = useState( '' );
const [ setupMethod, setSetupMethod ] = useState( 'qr-code' );
const [ inputs, setInputs ] = useState( Array( 6 ).fill( '' ) );
const [ statusWaiting, setStatusWaiting ] = useState( false );

// Fetch the data needed to setup TOTP.
useEffect( () => {
Expand All @@ -85,6 +88,9 @@ function Setup( { setSuccess } ) {
const code = inputs.join( '' );

try {
setError( '' );
setStatusWaiting( true );

await apiFetch( {
path: '/two-factor/1.0/totp/',
method: 'POST',
Expand All @@ -100,6 +106,8 @@ function Setup( { setSuccess } ) {
setSuccess( true );
} catch ( handleEnableError ) {
setError( handleEnableError.message );
} finally {
setStatusWaiting( false );
}
},
[ inputs, secretKey, userId, userRecord, setSuccess ]
Expand All @@ -123,23 +131,37 @@ function Setup( { setSuccess } ) {
when logging in to WordPress.org.
</p>

{ 'qr-code' === setupMethod && (
<SetupMethodQRCode setSetupMethod={ setSetupMethod } qrCodeUrl={ qrCodeUrl } />
) }

{ 'manual' === setupMethod && (
<SetupMethodManual setSetupMethod={ setSetupMethod } secretKey={ secretKey } />
{ statusWaiting ? (
<div className="wporg-2fa__process-status">
<Spinner />
</div>
) : (
<>
{ 'qr-code' === setupMethod && (
<SetupMethodQRCode
setSetupMethod={ setSetupMethod }
qrCodeUrl={ qrCodeUrl }
/>
) }

{ 'manual' === setupMethod && (
<SetupMethodManual
setSetupMethod={ setSetupMethod }
secretKey={ secretKey }
/>
) }

<SetupForm
handleEnable={ handleEnable }
qrCodeUrl={ qrCodeUrl }
secretKey={ secretKey }
inputs={ inputs }
setInputs={ setInputs }
error={ error }
setError={ setError }
/>
</>
) }

<SetupForm
handleEnable={ handleEnable }
qrCodeUrl={ qrCodeUrl }
secretKey={ secretKey }
inputs={ inputs }
setInputs={ setInputs }
error={ error }
setError={ setError }
/>
</Flex>
);
}
Expand Down Expand Up @@ -315,24 +337,48 @@ function Manage() {
setGlobalNotice,
} = useContext( GlobalContext );
const [ error, setError ] = useState( '' );
const [ disabling, setDisabling ] = useState( false );
const [ confirmingDisable, setConfirmingDisable ] = useState( false );

// Enable TOTP when button clicked.
const handleDisable = useCallback( async ( event ) => {
event.preventDefault();

try {
await apiFetch( {
path: '/two-factor/1.0/totp/',
method: 'DELETE',
data: { user_id: userRecord.record.id },
} );
/**
* Display the confirmation modal for disabling the TOTP provider.
*/
const showConfirmDisableModal = useCallback( () => {
setConfirmingDisable( true );
}, [] );

await refreshRecord( userRecord );
setGlobalNotice( 'Successfully disabled One Time Passwords.' );
} catch ( handleDisableError ) {
setError( handleDisableError.message );
}
} );
/**
* Remove the confirmation modal for disabling the TOTP provider.
*/
const hideConfirmDisableModal = useCallback( () => {
setConfirmingDisable( false );
}, [] );

// Disable TOTP when button clicked.
const handleDisable = useCallback(
async ( event ) => {
event.preventDefault();
setError( '' );
setDisabling( true );

try {
await apiFetch( {
path: '/two-factor/1.0/totp/',
method: 'DELETE',
data: { user_id: userRecord.record.id },
} );

await refreshRecord( userRecord );
setGlobalNotice( 'Successfully disabled your two-factor app.' );
} catch ( handleDisableError ) {
hideConfirmDisableModal();
setError( handleDisableError.message );
} finally {
setDisabling( false );
}
},
[ hideConfirmDisableModal, setGlobalNotice, userRecord ]
);

return (
<>
Expand All @@ -359,7 +405,7 @@ function Manage() {
</div>

<p className="wporg-2fa__submit-actions">
<Button isPrimary onClick={ handleDisable }>
<Button isPrimary onClick={ showConfirmDisableModal }>
Disable Two-Factor app
</Button>
</p>
Expand All @@ -370,6 +416,53 @@ function Manage() {
{ error }
</Notice>
) }

{ confirmingDisable && (
<ConfirmDisableApp
error={ error }
disabling={ disabling }
onClose={ hideConfirmDisableModal }
onConfirm={ handleDisable }
/>
) }
</>
);
}

/**
* Prompt the user to confirm they want to disable their two-factor app.
*
* @param {Object} props
* @param {Function} props.onConfirm
* @param {Function} props.onClose
* @param {boolean} props.disabling
*/
function ConfirmDisableApp( { onConfirm, onClose, disabling } ) {
return (
<Modal
title={ `Disable Two-Factor app` }
className="wporg-2fa__confirm-disable-app"
onRequestClose={ onClose }
>
<p className="wporg-2fa__screen-intro">
Are you sure you want to disable your two-factor app?
</p>

{ disabling ? (
<div className="wporg-2fa__process-status">
<Spinner />
</div>
) : (
<div className="wporg-2fa__submit-actions">
<Button variant="primary" onClick={ onConfirm }>
Disable
</Button>

<Button variant="tertiary" onClick={ onClose }>
Cancel
</Button>
</div>
) }
</Modal>
);
}
7 changes: 7 additions & 0 deletions settings/src/components/totp.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
.wporg-2fa__totp {
$min-container-height: 590px;

.wporg-2fa__totp_success {
min-height: $min-container-height;
}

.wporg-2fa__totp_setup-container {
position: relative;
min-height: $min-container-height;

> .components-button {
position: absolute;
Expand Down
26 changes: 13 additions & 13 deletions settings/src/components/webauthn/list-keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,20 +103,20 @@ function ConfirmRemoveKey( { keyToRemove, onConfirm, onClose, deleting, error }
Are you sure you want to remove the <code>{ keyToRemove.name }</code> security key?
</p>

<div className="wporg-2fa__submit-actions">
<Button variant="primary" onClick={ onConfirm }>
Remove { keyToRemove.name }
</Button>

<Button variant="tertiary" onClick={ onClose }>
Cancel
</Button>
</div>

{ deleting && (
<p className="wporg-2fa__webauthn-register-key-status">
{ deleting ? (
<div className="wporg-2fa__process-status">
<Spinner />
</p>
</div>
) : (
<div className="wporg-2fa__submit-actions">
<Button variant="primary" onClick={ onConfirm }>
Remove { keyToRemove.name }
</Button>

<Button variant="tertiary" onClick={ onClose }>
Cancel
</Button>
</div>
) }

{ error && (
Expand Down
Loading

0 comments on commit ea6a8c8

Please sign in to comment.