Skip to content

Commit

Permalink
Merge branch 'epic/ras-acc' into fix/cover-transaction-fee-refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelpeixe committed Dec 21, 2023
2 parents fe1ac2e + 384fc28 commit 368ec8e
Show file tree
Hide file tree
Showing 8 changed files with 300 additions and 122 deletions.
110 changes: 110 additions & 0 deletions assets/reader-activation/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ function domReady( callback ) {
document.addEventListener( 'DOMContentLoaded', callback );
}

/**
* Format time in MM:SS format.
*
* @param {number} time Time in seconds.
*/
function formatTime( time ) {
const minutes = Math.floor( time / 60 );
const seconds = time % 60;
return `${ minutes }:${ seconds < 10 ? '0' : '' }${ seconds }`;
}

/**
* Converts FormData into an object.
*
Expand All @@ -51,6 +62,7 @@ const convertFormDataToObject = ( formData, includedFields = [] ) =>
}, {} );

const SIGN_IN_MODAL_HASHES = [ 'signin_modal', 'register_modal' ];

let currentHash;

window.newspackRAS = window.newspackRAS || [];
Expand Down Expand Up @@ -215,6 +227,92 @@ window.newspackRAS.push( function ( readerActivation ) {
const passwordInput = form.querySelector( 'input[name="password"]' );
const submitButtons = form.querySelectorAll( '[type="submit"]' );
const closeButton = container.querySelector( 'button[data-close]' );
const backButtons = container.querySelectorAll( '[data-back]' );
const resendCodeButton = container.querySelector( '[data-resend-code]' );

backButtons.forEach( backButton => {
backButton.addEventListener( 'click', function ( ev ) {
ev.preventDefault();
setFormAction( 'link', true );
} );
} );

let otpTimerInterval;
let otpOriginalButtonText;
function handleOTPTimer() {
if ( otpTimerInterval ) {
clearInterval( otpTimerInterval );
}
if ( ! resendCodeButton ) {
return;
}
otpOriginalButtonText = resendCodeButton.textContent;
const updateButton = () => {
const remaining = readerActivation.getOTPTimeRemaining();
if ( remaining ) {
resendCodeButton.textContent = `${ otpOriginalButtonText } (${ formatTime(
remaining
) })`;
resendCodeButton.disabled = true;
} else {
resendCodeButton.textContent = otpOriginalButtonText;
resendCodeButton.disabled = false;
clearInterval( otpTimerInterval );
}
};
const remaining = readerActivation.getOTPTimeRemaining();
if ( remaining ) {
otpTimerInterval = setInterval( updateButton, 1000 );
updateButton();
}
}

if ( resendCodeButton ) {
handleOTPTimer();
resendCodeButton.addEventListener( 'click', function ( ev ) {
messageContentElement.innerHTML = '';
ev.preventDefault();
form.startLoginFlow();
const body = new FormData();
body.set( 'reader-activation-auth-form', 1 );
body.set( 'npe', emailInput.value );
body.set( 'action', 'link' );
readerActivation
.getCaptchaToken()
.then( captchaToken => {
if ( ! captchaToken ) {
return;
}
body.set( 'captcha_token', captchaToken );
} )
.catch( e => {
console.log( { e } );
} )
.finally( () => {
fetch( form.getAttribute( 'action' ) || window.location.pathname, {
method: 'POST',
headers: {
Accept: 'application/json',
},
body,
} )
.then( () => {
messageContentElement.innerHTML = newspack_reader_auth_labels.code_resent;
readerActivation.setOTPTimer();
} )
.catch( e => {
console.log( e );
} )
.finally( () => {
handleOTPTimer();
form.style.opacity = 1;
submitButtons.forEach( button => {
button.disabled = false;
} );
} );
} );
} );
}

if ( closeButton ) {
closeButton.addEventListener( 'click', function ( ev ) {
Expand Down Expand Up @@ -250,6 +348,15 @@ window.newspackRAS.push( function ( readerActivation ) {
if ( ! readerActivation.getOTPHash() ) {
return;
}
const emailAddressElements = container.querySelectorAll( '.email-address' );
emailAddressElements.forEach( element => {
element.textContent = readerActivation.getReader()?.email || '';
} );
// Focus on the first input.
const firstInput = container.querySelector( '.otp-field input[type="text"]' );
if ( firstInput ) {
firstInput.focus();
}
}
if ( [ 'link', 'pwd' ].includes( action ) ) {
readerActivation.setAuthStrategy( action );
Expand Down Expand Up @@ -430,6 +537,9 @@ window.newspackRAS.push( function ( readerActivation ) {
const otpHash = readerActivation.getOTPHash();
if ( otpHash && [ 'register', 'link' ].includes( action ) ) {
if ( status === 200 ) {
// Set OTP rate-limit timer
readerActivation.setOTPTimer();
handleOTPTimer();
setFormAction( 'otp' );
}
/** If action is link, suppress message and status so the OTP handles it. */
Expand Down
19 changes: 17 additions & 2 deletions assets/reader-activation/auth.scss
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,22 @@
}
}

button[type='button'] {
display: block;
background: transparent;
border: 1px solid wp-colors.$gray-200;
color: wp-colors.$gray-700;
margin-bottom: 0.8rem;
&:disabled {
background-color: wp-colors.$gray-100;
color: wp-colors.$gray-600;
}
&.back-button {
border: 0;
margin: 0;
}
}

.components-form {
&__field {
font-size: 1rem;
Expand All @@ -168,6 +184,7 @@
background-color: var( --newspack-cta-color );
color: var( --newspack-cta-contrast-color );
transition: background-color 150ms ease-in-out;
margin-bottom: 0.8rem;

&:hover,
&:focus {
Expand Down Expand Up @@ -327,8 +344,6 @@
}

&__response {
font-size: 0.8125em;
line-height: 1.2307;
&__content {
p {
margin: 0;
Expand Down
40 changes: 40 additions & 0 deletions assets/reader-activation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,43 @@ export function getOTPHash() {
return getCookie( 'np_otp_hash' );
}

/**
* OTP timer storage key.
*/
const OTP_TIMER_STORAGE_KEY = 'newspack_otp_timer';

/**
* Set the OTP timer to the current time.
*/
export function setOTPTimer() {
localStorage.setItem( OTP_TIMER_STORAGE_KEY, Math.floor( Date.now() / 1000 ) );
}

/**
* Clear the OTP timer.
*/
export function clearOTPTimer() {
localStorage.removeItem( OTP_TIMER_STORAGE_KEY );
}

/**
* Get the time remaining for the OTP timer.
*
* @return {number} Time remaining in seconds
*/
export function getOTPTimeRemaining() {
const timer = localStorage.getItem( OTP_TIMER_STORAGE_KEY );
if ( ! timer ) {
return 0;
}
const timeRemaining =
newspack_ras_config.otp_rate_interval - ( Math.floor( Date.now() / 1000 ) - timer );
if ( ! timeRemaining ) {
clearOTPTimer();
}
return timeRemaining > 0 ? timeRemaining : 0;
}

/**
* Authenticate reader using an OTP code.
*
Expand Down Expand Up @@ -338,6 +375,9 @@ const readerActivation = {
getReader,
hasAuthLink,
getOTPHash,
setOTPTimer,
clearOTPTimer,
getOTPTimeRemaining,
authenticateOTP,
setAuthStrategy,
getAuthStrategy,
Expand Down
66 changes: 44 additions & 22 deletions assets/wizards/readerRevenue/views/donation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ const FREQUENCIES: {
};
const FREQUENCY_SLUGS: FrequencySlug[] = Object.keys( FREQUENCIES ) as FrequencySlug[];

type FieldConfig = {
autocomplete: string;
class: string[];
label: string;
priority: number;
required: boolean;
type: string;
validate: string[];
};

type WizardData = {
donation_data:
| { errors: { [ key: string ]: string[] } }
Expand All @@ -61,16 +71,9 @@ type WizardData = {
status: string;
};
available_billing_fields: {
[ key: string ]: {
autocomplete: string;
class: string[];
label: string;
priority: number;
required: boolean;
type: string;
validate: string[];
};
[ key: string ]: FieldConfig;
};
order_notes_field: FieldConfig;
};

export const DonationAmounts = () => {
Expand Down Expand Up @@ -258,6 +261,8 @@ const BillingFields = () => {
} );

const availableFields = wizardData.available_billing_fields;
const orderNotesField = wizardData.order_notes_field;
console.log( wizardData );
if ( ! availableFields || ! Object.keys( availableFields ).length ) {
return null;
}
Expand All @@ -267,22 +272,23 @@ const BillingFields = () => {
: Object.keys( availableFields );

return (
<>
<Card noBorder headerActions>
<SectionHeader
title={ __( 'Billing Fields', 'newspack' ) }
description={ __(
'Configure which billing fields should be rendered on the donation form.',
'newspack'
) }
noMargin
/>
</Card>
<Grid columns={ 1 } gutter={ 16 }>
<SectionHeader
title={ __( 'Billing Fields', 'newspack-plugin' ) }
description={ __(
'Configure which billing fields should be shown by the checkout form. Fields marked with (*) are required if shown. Note that for shippable products, address fields will always be shown.',
'newspack-plugin'
) }
noMargin
/>
<Grid columns={ 3 } rowGap={ 16 }>
{ Object.keys( availableFields ).map( fieldKey => (
<CheckboxControl
key={ fieldKey }
label={ availableFields[ fieldKey ].label }
label={
availableFields[ fieldKey ].label +
( availableFields[ fieldKey ].required ? ' *' : '' )
}
checked={ billingFields.includes( fieldKey ) }
disabled={ fieldKey === 'billing_email' } // Email is always required.
onChange={ () => {
Expand All @@ -296,8 +302,23 @@ const BillingFields = () => {
} }
/>
) ) }
{ orderNotesField && (
<CheckboxControl
label={ orderNotesField.label }
checked={ billingFields.includes( 'order_comments' ) }
onChange={ () => {
let newFields = [ ...billingFields ];
if ( billingFields.includes( 'order_comments' ) ) {
newFields = newFields.filter( field => field !== 'order_comments' );
} else {
newFields = [ ...newFields, 'order_comments' ];
}
changeHandler( [ 'billingFields' ] )( newFields );
} }
/>
) }
</Grid>
</>
</Grid>
);
};

Expand Down Expand Up @@ -347,6 +368,7 @@ const Donation = () => {
</>
) }
<DonationAmounts />
<hr />
<BillingFields />
<div className="newspack-buttons-card">
<Button variant="primary" onClick={ onSave } href={ undefined }>
Expand Down
Loading

0 comments on commit 368ec8e

Please sign in to comment.