Skip to content

Commit

Permalink
Merge pull request #1717 from woocommerce/update/1033-phone-number-ve…
Browse files Browse the repository at this point in the history
…rification-status

Detect the verification status of the phone number to the contact information settings
  • Loading branch information
ianlin authored Oct 12, 2022
2 parents 8bf3aa3 + d484955 commit b47ad11
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 63 deletions.
15 changes: 2 additions & 13 deletions js/src/components/contact-information/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,24 +82,13 @@ export function ContactInformationPreview() {
* Renders a contact information section with specified initial state and texts.
*
* @param {Object} props React props.
* @param {Function} [props.onPhoneNumberVerified] Called when the phone number is verified.
* @param {Function} [props.onPhoneNumberVerified] Called when the phone number is verified or has been verified.
* @fires gla_documentation_link_click with `{ context: 'setup-mc-contact-information', link_id: 'contact-information-read-more', href: 'https://docs.woocommerce.com/document/google-listings-and-ads/#contact-information' }`
* @fires gla_documentation_link_click with `{ context: 'settings-no-phone-number-notice', link_id: 'contact-information-read-more', href: 'https://docs.woocommerce.com/document/google-listings-and-ads/#contact-information' }`
* @fires gla_documentation_link_click with `{ context: 'settings-no-store-address-notice', link_id: 'contact-information-read-more', href: 'https://docs.woocommerce.com/document/google-listings-and-ads/#contact-information' }`
*/
const ContactInformation = ( { onPhoneNumberVerified } ) => {
const phone = useGoogleMCPhoneNumber();

/**
* Since it is still lacking the phone verification state,
* all onboarding accounts are considered unverified phone numbers.
*
* TODO: replace the code at next line back to the original logic with
* `const initEditing = null;`
* after the phone verification state can be detected.
*/
const initEditing = true;

const title = mcTitle;
const trackContext = 'setup-mc-contact-information';

Expand Down Expand Up @@ -127,7 +116,7 @@ const ContactInformation = ( { onPhoneNumberVerified } ) => {
<PhoneNumberCard
view="setup-mc"
phoneNumber={ phone }
initEditing={ initEditing }
initEditing={ null }
onPhoneNumberVerified={ onPhoneNumberVerified }
/>
<StoreAddressCard />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { __ } from '@wordpress/i18n';
import { APPEARANCE } from '.~/components/account-card';
import useGoogleMCPhoneNumber from '.~/hooks/useGoogleMCPhoneNumber';
import ContactInformationPreviewCard from '../contact-information-preview-card';
import './phone-number-card-preview.scss';

/**
* Triggered when phone number edit button is clicked.
Expand All @@ -35,7 +36,21 @@ export function PhoneNumberCardPreview( { editHref, learnMore } ) {

if ( loaded ) {
if ( data.isValid ) {
content = data.display;
const verificationStatus = data.isVerified ? (
<div className="gla-phone-number-card-preview__verified-status">
{ __( 'Verified', 'google-listings-and-ads' ) }
</div>
) : (
<div className="gla-phone-number-card-preview__unverified-status">
{ __( 'Unverified', 'google-listings-and-ads' ) }
</div>
);
content = (
<>
{ data.display }
{ verificationStatus }
</>
);
} else {
warning = __(
'Please add your phone number',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.gla-contact-info-preview-card {
.gla-account-card__description {
gap: $grid-unit-05;
}
}

.gla-phone-number-card-preview {
&__verified-status {
color: $alert-green;
}

&__unverified-status {
color: $alert-red;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { useState, useEffect } from '@wordpress/element';
import { useState, useEffect, useRef } from '@wordpress/element';
import { CardDivider } from '@wordpress/components';
import { Spinner } from '@woocommerce/components';

Expand Down Expand Up @@ -101,10 +101,10 @@ function EditPhoneNumberCard( { phoneNumber, onPhoneNumberVerified } ) {
* @param {boolean|null} [props.initEditing=null] Specify the inital UI state.
* `true`: initialize with the editing UI.
* `false`: initialize with the viewing UI.
* `null`: determine the initial UI state according to the `data.isValid` after the `phoneNumber` loaded.
* `null`: determine the initial UI state according to the `data.isVerified` after the `phoneNumber` loaded.
* @param {Function} [props.onEditClick] Called when clicking on "Edit" button.
* If this callback is omitted, it will enter edit mode when clicking on "Edit" button.
* @param {Function} [props.onPhoneNumberVerified] Called when the phone number is verified in edit mode.
* @param {Function} [props.onPhoneNumberVerified] Called when the phone number is verified or has been verified.
*
* @fires gla_mc_phone_number_edit_button_click
*/
Expand All @@ -117,14 +117,27 @@ const PhoneNumberCard = ( {
} ) => {
const { loaded, data } = phoneNumber;
const [ isEditing, setEditing ] = useState( initEditing );
const onPhoneNumberVerifiedRef = useRef();
onPhoneNumberVerifiedRef.current = onPhoneNumberVerified;

const { isVerified } = data;

// Handle the initial UI state of `initEditing = null`.
// The `isEditing` state is on hold. Determine it after the `phoneNumber` loaded.
useEffect( () => {
if ( loaded && isEditing === null ) {
setEditing( ! data.isValid );
setEditing( ! isVerified );
}
}, [ loaded, isVerified, isEditing ] );

// If `initEditing` is true, EditPhoneNumberCard takes care of the call of `onPhoneNumberVerified`
// after the phone number is verified. If `initEditing` is false or null, this useEffect handles
// the call of `onPhoneNumberVerified` when the loaded phone number has already been verified.
useEffect( () => {
if ( loaded && initEditing !== true && isVerified ) {
onPhoneNumberVerifiedRef.current();
}
}, [ loaded, data.isValid, isEditing ] );
}, [ loaded, isVerified, initEditing ] );

// Return a simple loading AccountCard since the initial edit state is unknown before loaded.
if ( isEditing === null ) {
Expand Down
37 changes: 36 additions & 1 deletion js/src/data/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import createSelector from 'rememo';
import { STORE_KEY } from './constants';
import { getReportQuery, getReportKey, getPerformanceQuery } from './utils';

/**
* @typedef {import('.~/data/actions').CountryCode} CountryCode
*/

export const getShippingRates = ( state ) => {
return state.mc.shipping.rates;
};
Expand Down Expand Up @@ -74,11 +78,42 @@ export const getExistingGoogleAdsAccounts = ( state ) => {
return state.mc.accounts.existing_ads;
};

/**
* @typedef {Object} Address
* @property {string|null} street_address Street-level part of the address. `null` when empty.
* @property {string|null} locality City, town or commune. `null` when empty.
* @property {string|null} region Top-level administrative subdivision of the country. `null` when empty.
* @property {string|null} postal_code Postal code or ZIP. `null` when empty.
* @property {CountryCode} country Two-letter country code in ISO 3166-1 alpha-2 format. Example: 'US'.
*
* @typedef {Object} ContactInformation
* @property {number} id The Google Merchant Center account ID.
* @property {string|null} phone_number The phone number in international format associated with the Google Merchant Center account. Example: '+12133734253'. `null` if the phone number is not yet set.
* @property {'verified'|'unverified'|null} phone_verification_status The verification status of the phone number associated with the Google Merchant Center account. `null` if the phone number is not yet set.
* @property {Address|null} mc_address The address associated with the Google Merchant Center account. `null` if the address is not yet set.
* @property {Address|null} wc_address The WooCommerce store address. `null` if the address is not yet set.
* @property {boolean} is_mc_address_different Whether the Google Merchant Center account address is different than the WooCommerce store address.
* @property {string[]} wc_address_errors The errors associated with the WooCommerce store address.
*/

/**
* Select the state of contact information associated with the Google Merchant Center account.
*
* @param {Object} state The current store state will be injected by `wp.data`.
* @return {ContactInformation|null} The contact information associated with the Google Merchant Center account. It would return `null` before the data is fetched.
*/
export const getGoogleMCContactInformation = ( state ) => {
return state.mc.contact;
};

// Create another selector to separate the `hasFinishedResolution` state with `getGoogleMCContactInformation`.
/**
* Select the state of phone number associated with the Google Merchant Center account.
*
* Create another selector to separate the `hasFinishedResolution` state with `getGoogleMCContactInformation`.
*
* @param {Object} state The current store state will be injected by `wp.data`.
* @return {{ data: ContactInformation|null, loaded: boolean }} The payload of contact information associated with the Google Merchant Center account and its loaded state.
*/
export const getGoogleMCPhoneNumber = createRegistrySelector(
( select ) => ( state ) => {
const selector = select( STORE_KEY );
Expand Down
6 changes: 5 additions & 1 deletion js/src/hooks/useGoogleMCPhoneNumber.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const emptyData = {
countryCallingCode: '',
nationalNumber: '',
isValid: false,
isVerified: false,
display: '',
};

Expand All @@ -29,13 +30,14 @@ const emptyData = {
* @property {string} countryCallingCode The country calling code. Example: '1'.
* @property {string} nationalNumber The national (significant) number. Example: '2133734253'.
* @property {boolean} isValid Whether the phone number is valid.
* @property {boolean} isVerified Whether the phone number is verified.
* @property {string} display The phone number string in international format. Example: '+1 213 373 4253'.
*/

/**
* A hook to load user's phone number data from Google Merchant Center.
*
* @return {PhoneNumber} [description]
* @return {PhoneNumber} The payload of parsed phone number associated with the Google Merchant Center account and its loaded state.
*/
export default function useGoogleMCPhoneNumber() {
return useSelect( ( select ) => {
Expand All @@ -50,6 +52,8 @@ export default function useGoogleMCPhoneNumber() {
data = {
...parsed,
isValid: parsed.isValid(),
isVerified:
contact.phone_verification_status === 'verified',
display: parsed.formatInternational(),
};
delete data.metadata;
Expand Down
17 changes: 2 additions & 15 deletions js/src/setup-mc/setup-stepper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@ import useMCSetup from '.~/hooks/useMCSetup';
import stepNameKeyMap from './stepNameKeyMap';

const SetupStepper = () => {
const {
hasFinishedResolution,
data: mcSetup,
invalidateResolution: mcSetupInvalidateResolution,
} = useMCSetup();
const { hasFinishedResolution, data: mcSetup } = useMCSetup();

if ( ! hasFinishedResolution && ! mcSetup ) {
return <AppSpinner />;
Expand All @@ -36,16 +32,7 @@ const SetupStepper = () => {
return null;
}

const handleRefetchSavedStep = () => {
mcSetupInvalidateResolution();
};

return (
<SavedSetupStepper
savedStep={ stepNameKeyMap[ step ] }
onRefetchSavedStep={ handleRefetchSavedStep }
/>
);
return <SavedSetupStepper savedStep={ stepNameKeyMap[ step ] } />;
};

export default SetupStepper;
9 changes: 3 additions & 6 deletions js/src/setup-mc/setup-stepper/saved-setup-stepper.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,9 @@ import stepNameKeyMap from './stepNameKeyMap';
/**
* @param {Object} props React props
* @param {string} [props.savedStep] A saved step overriding the current step
* @param {Function} [props.onRefetchSavedStep] Callback when Saved Step is updated
* @fires gla_setup_mc with `{ target: 'step1_continue' | 'step2_continue' | 'step3_continue', trigger: 'click' }`.
*/
const SavedSetupStepper = ( { savedStep, onRefetchSavedStep = () => {} } ) => {
const SavedSetupStepper = ( { savedStep } ) => {
const [ step, setStep ] = useState( savedStep );

const { settings } = useSettings();
Expand Down Expand Up @@ -83,7 +82,6 @@ const SavedSetupStepper = ( { savedStep, onRefetchSavedStep = () => {} } ) => {
trigger: 'click',
} );
setStep( stepNameKeyMap.product_listings );
onRefetchSavedStep();
};

const handleSetupListingsContinue = () => {
Expand All @@ -92,7 +90,6 @@ const SavedSetupStepper = ( { savedStep, onRefetchSavedStep = () => {} } ) => {
trigger: 'click',
} );
setStep( stepNameKeyMap.store_requirements );
onRefetchSavedStep();
};

const handleStoreRequirementsContinue = () => {
Expand All @@ -101,11 +98,11 @@ const SavedSetupStepper = ( { savedStep, onRefetchSavedStep = () => {} } ) => {
trigger: 'click',
} );
setStep( stepNameKeyMap.paid_ads );
onRefetchSavedStep();
};

const handleStepClick = ( stepKey ) => {
if ( Number( stepKey ) <= Number( savedStep ) ) {
// Only allow going back to the previous steps.
if ( Number( stepKey ) < Number( step ) ) {
setStep( stepKey );
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,35 +124,40 @@ protected function get_contact_information_endpoint_edit_callback(): callable {
*/
protected function get_schema_properties(): array {
return [
'id' => [
'id' => [
'type' => 'integer',
'description' => __( 'The Merchant Center account ID.', 'google-listings-and-ads' ),
'context' => [ 'view', 'edit' ],
'validate_callback' => 'rest_validate_request_arg',
],
'phone_number' => [
'phone_number' => [
'type' => 'string',
'description' => __( 'The phone number associated with the Merchant Center account.', 'google-listings-and-ads' ),
'context' => [ 'view' ],
],
'mc_address' => [
'phone_verification_status' => [
'type' => 'string',
'description' => __( 'The verification status of the phone number associated with the Merchant Center account.', 'google-listings-and-ads' ),
'context' => [ 'view' ],
],
'mc_address' => [
'type' => 'object',
'description' => __( 'The address associated with the Merchant Center account.', 'google-listings-and-ads' ),
'context' => [ 'view' ],
'properties' => $this->get_address_schema(),
],
'wc_address' => [
'wc_address' => [
'type' => 'object',
'description' => __( 'The WooCommerce store address.', 'google-listings-and-ads' ),
'context' => [ 'view' ],
'properties' => $this->get_address_schema(),
],
'is_mc_address_different' => [
'is_mc_address_different' => [
'type' => 'boolean',
'description' => __( 'Whether the Merchant Center account address is different than the WooCommerce store address.', 'google-listings-and-ads' ),
'context' => [ 'view' ],
],
'wc_address_errors' => [
'wc_address_errors' => [
'type' => 'array',
'description' => __( 'The errors associated with the WooCommerce address', 'google-listings-and-ads' ),
'context' => [ 'view' ],
Expand Down Expand Up @@ -215,10 +220,11 @@ public function get_update_args(): array {
* @return Response
*/
protected function get_contact_information_response( ?AccountBusinessInformation $contact_information, Request $request ): Response {
$phone_number = null;
$mc_address = null;
$wc_address = null;
$is_address_diff = false;
$phone_number = null;
$phone_verification_status = null;
$mc_address = null;
$wc_address = null;
$is_address_diff = false;

if ( $this->settings->get_store_address() instanceof AccountAddress ) {
$wc_address = $this->settings->get_store_address();
Expand All @@ -228,7 +234,8 @@ protected function get_contact_information_response( ?AccountBusinessInformation
if ( $contact_information instanceof AccountBusinessInformation ) {
if ( ! empty( $contact_information->getPhoneNumber() ) ) {
try {
$phone_number = PhoneNumber::cast( $contact_information->getPhoneNumber() )->get();
$phone_number = PhoneNumber::cast( $contact_information->getPhoneNumber() )->get();
$phone_verification_status = strtolower( $contact_information->getPhoneVerificationStatus() );
} catch ( InvalidValue $exception ) {
// log and fail silently
do_action( 'woocommerce_gla_exception', $exception, __METHOD__ );
Expand All @@ -249,12 +256,13 @@ protected function get_contact_information_response( ?AccountBusinessInformation

return $this->prepare_item_for_response(
[
'id' => $this->options->get_merchant_id(),
'phone_number' => $phone_number,
'mc_address' => self::serialize_address( $mc_address ),
'wc_address' => self::serialize_address( $wc_address ),
'is_mc_address_different' => $is_address_diff,
'wc_address_errors' => $wc_address_errors,
'id' => $this->options->get_merchant_id(),
'phone_number' => $phone_number,
'phone_verification_status' => $phone_verification_status,
'mc_address' => self::serialize_address( $mc_address ),
'wc_address' => self::serialize_address( $wc_address ),
'is_mc_address_different' => $is_address_diff,
'wc_address_errors' => $wc_address_errors,
],
$request
);
Expand Down
Loading

0 comments on commit b47ad11

Please sign in to comment.