Skip to content

Commit

Permalink
Scan pages sequentially and visualize current scan state
Browse files Browse the repository at this point in the history
  • Loading branch information
delawski committed Sep 22, 2021
1 parent efc6d2e commit bbbaaf5
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 48 deletions.
81 changes: 54 additions & 27 deletions assets/src/components/site-scan-context-provider/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* WordPress dependencies
*/
import { createContext, useEffect, useRef, useState } from '@wordpress/element';
import { getQueryArg } from '@wordpress/url';
import apiFetch from '@wordpress/api-fetch';

/**
Expand All @@ -17,6 +16,12 @@ import { useAsyncError } from '../../utils/use-async-error';

export const SiteScan = createContext();

const SITE_SCAN_STATE_INITIALIZING = 'SITE_SCAN_STATE_INITIALIZING';
const SITE_SCAN_STATE_READY = 'SITE_SCAN_STATE_READY';
const SITE_SCAN_STATE_IDLE = 'SITE_SCAN_STATE_IDLE';
const SITE_SCAN_STATE_IN_PROGRESS = 'SITE_SCAN_STATE_IN_PROGRESS';
const SITE_SCAN_STATE_COMPLETE = 'SITE_SCAN_STATE_COMPLETE';

/**
* Context provider for site scanning.
*
Expand All @@ -28,10 +33,12 @@ export function SiteScanContextProvider( {
children,
scannableUrlsRestPath,
} ) {
const [ themeIssues, setThemeIssues ] = useState( null );
const [ pluginIssues, setPluginIssues ] = useState( null );
const [ scanningSite, setScanningSite ] = useState( true );
const [ themeIssues, setThemeIssues ] = useState( [] );
const [ pluginIssues, setPluginIssues ] = useState( [] );
const [ siteScanState, setSiteScanState ] = useState( SITE_SCAN_STATE_INITIALIZING );
const [ currentlyScannedUrlIndex, setCurrentlyScannedUrlIndex ] = useState( 0 );
const [ fetchingScannableUrls, setFetchingScannableUrls ] = useState( false );
const [ fetchedScannableUrls, setFetchedScannableUrls ] = useState( false );
const [ scannableUrls, setScannableUrls ] = useState( [] );
const { setAsyncError } = useAsyncError();

Expand All @@ -42,7 +49,7 @@ export function SiteScanContextProvider( {
}, [] );

useEffect( () => {
if ( fetchingScannableUrls || scannableUrls.length > 0 ) {
if ( fetchingScannableUrls || fetchedScannableUrls ) {
return;
}

Expand All @@ -53,57 +60,77 @@ export function SiteScanContextProvider( {
setFetchingScannableUrls( true );

try {
const fetchedScannableUrls = await apiFetch( {
const response = await apiFetch( {
path: scannableUrlsRestPath,
} );

if ( true === hasUnmounted.current ) {
return;
}

setScannableUrls( fetchedScannableUrls );
setScannableUrls( response );
setSiteScanState( SITE_SCAN_STATE_READY );
setFetchedScannableUrls( true );
} catch ( e ) {
setAsyncError( e );
return;
}

setFetchingScannableUrls( false );
} )();
}, [ fetchingScannableUrls, scannableUrlsRestPath, scannableUrls.length, setAsyncError ] );
}, [ fetchedScannableUrls, fetchingScannableUrls, scannableUrlsRestPath, setAsyncError ] );

/**
* @todo Note: The following effects will be updated for version 2.1 when site scan is implemented in the wizard. For now,
* we will keep themeIssues and pluginIssues set to null, emulating an unsuccessful site scan. The wizard will then make
* a mode recommendation based only on how the user has answered the technical question.
* Scan site URLs sequentially.
*/
useEffect( () => {
if ( ! scanningSite && ! themeIssues ) {
setThemeIssues( getQueryArg( global.location.href, 'amp-theme-issues' ) ? [ 'Theme issue 1' ] : null ); // URL param is for testing.
if ( siteScanState !== SITE_SCAN_STATE_IDLE ) {
return;
}
}, [ scanningSite, themeIssues ] );

// See note above.
useEffect( () => {
if ( ! scanningSite && ! pluginIssues ) {
setPluginIssues( getQueryArg( global.location.href, 'amp-plugin-issues' ) ? [ 'Plugin issue 1' ] : null ); // URL param is for testing.
}
}, [ scanningSite, pluginIssues ] );
/**
* Validates the next URL in the queue.
*/
( async () => {
setSiteScanState( SITE_SCAN_STATE_IN_PROGRESS );

// See note above.
useEffect( () => {
if ( true === scanningSite ) {
setScanningSite( false );
}
}, [ scanningSite ] );
try {
const { validate_url: validateUrl } = scannableUrls[ currentlyScannedUrlIndex ];
const validationResults = await apiFetch( { url: validateUrl } );

if ( true === hasUnmounted.current ) {
return;
}

setPluginIssues( ( issues ) => [ ...issues, validationResults ] );
} catch ( e ) {
setAsyncError( e );
return;
}

/**
* Finish the scan if there are no more URLs to validate.
*/
if ( currentlyScannedUrlIndex === scannableUrls.length - 1 ) {
setSiteScanState( SITE_SCAN_STATE_COMPLETE );
} else {
setCurrentlyScannedUrlIndex( ( index ) => index + 1 );
setSiteScanState( SITE_SCAN_STATE_IDLE );
}
} )();
}, [ currentlyScannedUrlIndex, scannableUrls, setAsyncError, siteScanState ] );

return (
<SiteScan.Provider
value={
{
canScanSite: siteScanState === SITE_SCAN_STATE_READY,
currentlyScannedUrlIndex,
fetchingScannableUrls,
pluginIssues,
scanningSite,
scannableUrls,
siteScanComplete: siteScanState === SITE_SCAN_STATE_COMPLETE,
startSiteScan: () => setSiteScanState( SITE_SCAN_STATE_IDLE ),
themeIssues,
}
}
Expand Down
31 changes: 31 additions & 0 deletions assets/src/onboarding-wizard/pages/site-scan/complete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { Selectable } from '../../../components/selectable';
import { IconLandscapeHillsCogs } from '../../../components/svg/landscape-hills-cogs';

/**
* Screen with site scan summary.
*/
export function SiteScanComplete() {
return (
<div className="site-scan">
<Selectable>
<div className="site-scan__header">
<IconLandscapeHillsCogs />
<p className="site-scan__heading">
{ __( 'Scan complete', 'amp' ) }
</p>
</div>
<p>
{ __( 'Site scan found issues on your site. Proceed to the next step to follow recommendations for choosing a template mode.', 'amp' ) }
</p>
</Selectable>
</div>
);
}
53 changes: 53 additions & 0 deletions assets/src/onboarding-wizard/pages/site-scan/in-progress.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { useContext } from '@wordpress/element';

/**
* Internal dependencies
*/
import { SiteScan as SiteScanContext } from '../../../components/site-scan-context-provider';
import { Selectable } from '../../../components/selectable';
import { IconLandscapeHillsCogs } from '../../../components/svg/landscape-hills-cogs';
import { ProgressBar } from '../../../components/progress-bar';

/**
* Screen for visualizing a site scan progress state.
*/
export function SiteScanInProgress() {
const {
currentlyScannedUrlIndex,
scannableUrls,
siteScanComplete,
} = useContext( SiteScanContext );

return (
<div className="site-scan">
<Selectable>
<div className="site-scan__header">
<IconLandscapeHillsCogs />
<p className="site-scan__heading">
{ __( 'Please wait a minute…', 'amp' ) }
</p>
</div>
<p>
{ __( 'Site scan is checking if there are AMP compatibility issues with your active theme and plugins. We’ll then recommend how to use the AMP plugin.', 'amp' ) }
</p>
<ProgressBar value={ siteScanComplete
? 100
: ( currentlyScannedUrlIndex / scannableUrls.length * 100 )
} />
<p>
{ sprintf(
// translators: 1: currently scanned URL index; 2: scannable URLs count; 3: scanned page type.
__( 'Scanning %1$d/%2$d URLs: Checking %3$s…', 'amp' ),
currentlyScannedUrlIndex + 1,
scannableUrls.length,
scannableUrls[ currentlyScannedUrlIndex ]?.type,
) }
</p>
</Selectable>
</div>
);
}
45 changes: 24 additions & 21 deletions assets/src/onboarding-wizard/pages/site-scan/index.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,49 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { useContext, useEffect } from '@wordpress/element';

/**
* Internal dependencies
*/
import './style.scss';
import { Navigation } from '../../components/navigation-context-provider';
import { Selectable } from '../../../components/selectable';
import { IconLandscapeHillsCogs } from '../../../components/svg/landscape-hills-cogs';
import { SiteScan as SiteScanContext } from '../../../components/site-scan-context-provider';
import { SiteScanComplete } from './complete';
import { SiteScanInProgress } from './in-progress';

/**
* Screen for visualizing a site scan.
*/
export function SiteScan() {
const { setCanGoForward } = useContext( Navigation );
const {
canScanSite,
startSiteScan,
siteScanComplete,
} = useContext( SiteScanContext );

/**
* Start site scan.
*/
useEffect( () => {
if ( canScanSite ) {
startSiteScan();
}
}, [ canScanSite, startSiteScan ] );

/**
* Allow moving forward.
*/
useEffect( () => {
// @todo: Allow moving forward once the scan is complete.
if ( true ) {
if ( siteScanComplete ) {
setCanGoForward( true );
}
}, [ setCanGoForward ] );
}, [ setCanGoForward, siteScanComplete ] );

if ( siteScanComplete ) {
return <SiteScanComplete />;
}

return (
<div className="site-scan">
<Selectable>
<div className="site-scan__header">
<IconLandscapeHillsCogs />
<p className="site-scan__heading">
{ __( 'Please wait a minute ...', 'amp' ) }
</p>
</div>
<p>
{ __( 'Site scan is checking if there are AMP compatibility issues with your active theme and plugins. We’ll then recommend how to use the AMP plugin.', 'amp' ) }
</p>
</Selectable>
</div>
);
return <SiteScanInProgress />;
}

0 comments on commit bbbaaf5

Please sign in to comment.