Skip to content

Commit

Permalink
Add/onboarding wix flow (#58782)
Browse files Browse the repository at this point in the history
* Export calculateProgress method

* Onboarding: introduce Wix flow

* Introduce util class

* Onboarding: create done button component

* Implement Wix import logic

* Redirect user to the newly created onboarding Wix flow
The importer URL will remain the same if the Wix onboarding feature flag is turned off.

* Fix issue with getting query params

* Handle edge case, don't run import process on the browser's back

* Fix issue with showing progress bar value change

* Fix type checking issue
  • Loading branch information
bogiii committed Dec 9, 2021
1 parent 2606ee6 commit 0bb446b
Show file tree
Hide file tree
Showing 10 changed files with 358 additions and 51 deletions.
2 changes: 1 addition & 1 deletion client/my-sites/importer/importing-pane.jsx
Expand Up @@ -32,7 +32,7 @@ const sum = ( a, b ) => a + b;
* …
* }
*/
const calculateProgress = ( progress ) => {
export const calculateProgress = ( progress ) => {
const { attachment = {} } = progress;

if ( attachment.total > 0 && attachment.completed >= 0 ) {
Expand Down
162 changes: 141 additions & 21 deletions client/signup/steps/import-from/index.tsx
@@ -1,38 +1,158 @@
import { isEnabled } from '@automattic/calypso-config';
import React from 'react';
import { Title } from '@automattic/onboarding';
import page from 'page';
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { LoadingEllipsis } from 'calypso/components/loading-ellipsis';
import { Interval, EVERY_FIVE_SECONDS } from 'calypso/lib/interval';
import { decodeURIComponentIfValid } from 'calypso/lib/url';
import StepWrapper from 'calypso/signup/step-wrapper';
import { fetchImporterState } from 'calypso/state/imports/actions';
import {
getImporterStatusForSiteId,
isImporterStatusHydrated,
} from 'calypso/state/imports/selectors';
import { canCurrentUser } from 'calypso/state/selectors/can-current-user';
import { getSiteId } from 'calypso/state/sites/selectors';
import { Importer, QueryObject, ImportJob } from './types';
import { getImporterTypeForEngine } from './util';
import WixImporter from './wix';

import './style.scss';

/* eslint-disable wpcalypso/jsx-classname-namespace */

interface Props {
path: string;
stepName: string;
stepSectionName: string;
queryObject: {
temp: string;
};
queryObject: QueryObject;
siteId: number;
siteSlug: string;
fromSite: string;
canImport: boolean;
isImporterStatusHydrated: boolean;
siteImports: ImportJob[];
fetchImporterState: ( siteId: number ) => void;
}

const ImportOnboardingFrom: React.FunctionComponent< Props > = ( props ) => {
const { stepSectionName, queryObject } = props;
const {
stepSectionName,
siteId,
canImport,
siteSlug,
siteImports,
isImporterStatusHydrated,
fromSite,
} = props;

/**
↓ Fields
*/
const engine: Importer = stepSectionName.toLowerCase() as Importer;
const [ runImportInitially, setRunImportInitially ] = useState( false );
const getImportJob = ( engine: Importer ): ImportJob | undefined => {
return siteImports.find( ( x ) => x.type === getImporterTypeForEngine( engine ) );
};

/**
↓ Effects
*/
useEffect( fetchImporters, [ siteId ] );
useEffect( checkInitialRunState, [ siteId ] );

/**
↓ Methods
*/
function fetchImporters() {
siteId && props.fetchImporterState( siteId );
}

function isLoading() {
return ! isImporterStatusHydrated;
}

function hasPermission(): boolean {
return canImport;
}

function checkInitialRunState() {
const searchParams = new URLSearchParams( window.location.search );

// run query param indicates that the import process can be run immediately,
// but before proceeding, remove it from the URL path
// because of the browser's back edge case
if ( searchParams.get( 'run' ) === 'true' ) {
setRunImportInitially( true );
page.replace( props.path.replace( '&run=true', '' ).replace( 'run=true', '' ) );
}
}

return (
<StepWrapper
flowName={ 'import-from' }
hideSkip={ true }
hideBack={ true }
hideNext={ true }
hideFormattedHeader={ true }
stepContent={
<div className="import__onboarding-page">
{ stepSectionName === 'wix' && isEnabled( 'gutenboarding/import-from-wix' ) && (
<WixImporter queryObject={ queryObject } />
) }
</div>
}
/>
<>
<Interval onTick={ fetchImporters } period={ EVERY_FIVE_SECONDS } />

<StepWrapper
flowName={ 'import-from' }
hideSkip={ true }
hideBack={ true }
hideNext={ true }
hideFormattedHeader={ true }
stepContent={
<div className="import__onboarding-page import-layout__center">
<div className="import-layout__center">
{ ( () => {
/**
* Loading screen
*/
if ( isLoading() ) {
return <LoadingEllipsis />;
} else if ( ! hasPermission() ) {
/**
* Permission screen
*/
return <Title>You are not authorized to view this page</Title>;
} else if ( engine === 'wix' && isEnabled( 'gutenboarding/import-from-wix' ) ) {
/**
* Wix importer
*/
return (
<WixImporter
job={ getImportJob( engine ) }
run={ runImportInitially }
siteId={ siteId }
siteSlug={ siteSlug }
fromSite={ fromSite }
/>
);
}
} )() }
</div>
</div>
}
/>
</>
);
};

export default ImportOnboardingFrom;
export default connect(
( state ) => {
const searchParams = new URLSearchParams( window.location.search );

const siteSlug = decodeURIComponentIfValid( searchParams.get( 'to' ) );
const fromSite = decodeURIComponentIfValid( searchParams.get( 'from' ) );
const siteId = getSiteId( state, siteSlug ) as number;

return {
siteId,
siteSlug,
fromSite,
siteImports: getImporterStatusForSiteId( state, siteId ),
isImporterStatusHydrated: isImporterStatusHydrated( state ),
canImport: canCurrentUser( state, siteId, 'manage_options' ),
};
},
{
fetchImporterState,
}
)( ImportOnboardingFrom );
4 changes: 4 additions & 0 deletions client/signup/steps/import-from/style.scss
Expand Up @@ -9,4 +9,8 @@
min-width: 465px;
}
}

.wpcom__loading-ellipsis {
margin: auto;
}
}
33 changes: 33 additions & 0 deletions client/signup/steps/import-from/types.ts
@@ -0,0 +1,33 @@
export type Importer = 'wix' | 'medium';
export type QueryObject = {
from: string;
to: string;
};

export interface ImportJob {
importerId: string;
importerState: string;
type: string;
site: { ID: number };
customData: { [ key: string ]: any };
errorData: {
type: string;
description: string;
};
progress: {
page: { completed: number; total: number };
post: { completed: number; total: number };
comment: { completed: number; total: number };
attachment: { completed: number; total: number };
};
}

export interface ImportJobParams {
engine: Importer;
importerStatus: ImportJob;
params: { engine: Importer };
site: { ID: number };
targetSiteUrl: string;
supportedContent: string[];
unsupportedContent: string[];
}
3 changes: 3 additions & 0 deletions client/signup/steps/import-from/util.ts
@@ -0,0 +1,3 @@
import { Importer } from './types';

export const getImporterTypeForEngine = ( engine: Importer ) => `importer-type-${ engine }`;
30 changes: 30 additions & 0 deletions client/signup/steps/import-from/wix/done-button.tsx
@@ -0,0 +1,30 @@
import { NextButton } from '@automattic/onboarding';
import { useI18n } from '@wordpress/react-i18n';
import page from 'page';
import React from 'react';
import { ImportJob } from '../types';

interface Props {
job: ImportJob;
siteId: number;
siteSlug: string;
resetImport: ( siteId: number, importerId: string ) => void;
}
const DoneButton: React.FunctionComponent< Props > = ( props ) => {
const { __ } = useI18n();
const { job, siteId, siteSlug, resetImport } = props;

function onButtonClick() {
redirectToSiteView();
resetImport( siteId, job.importerId );
}

function redirectToSiteView() {
const destination = '/view/' + ( siteSlug || '' );
page( destination );
}

return <NextButton onClick={ onButtonClick }>{ __( 'View site' ) }</NextButton>;
};

export default DoneButton;

0 comments on commit 0bb446b

Please sign in to comment.