Skip to content

Commit

Permalink
Backup: decouple connection screens from useConnection hook (#38948)
Browse files Browse the repository at this point in the history
* Add backup connection screen components

* Add useBackupProductInfo hook

This hook will fetch and manage backup product pricing information needed on connection screens

* Rename useBackupProductInfo import

* Refactor useConnection hook, decouple components

Moved BackupConnectionScreen and BackupSecondaryAdminConnectionScreen away from this hook.

* Update useConnection hook jsdoc

* Update usage of useConnection hook

- Replaced destructured array usage with direct connectionStatus assignment.
- Use BackupConnectionScreen and BackupSecondaryAdminConnectionScreen directly.

* Delete assets

- These assets were moved inside backup-connection-screen component.
- connect-right.png wasn’t in use

* changelog

* Fix lint issue
  • Loading branch information
Initsogar authored Aug 20, 2024
1 parent 7d77b83 commit a950c3f
Show file tree
Hide file tree
Showing 14 changed files with 162 additions and 131 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fixed

Decoupled backup connection screens from useConnection hook to avoid unnecessary loading and prevent duplicated API calls.
2 changes: 1 addition & 1 deletion projects/packages/backup/src/js/components/Admin/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const Header = () => {
};

const useShowActivateLicenseLink = () => {
const [ connectionStatus ] = useConnection();
const connectionStatus = useConnection();
const isFullyConnected = useIsFullyConnected();
const { capabilitiesLoaded, hasBackupPlan } = useCapabilities();

Expand Down
4 changes: 2 additions & 2 deletions projects/packages/backup/src/js/components/Admin/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useEffect, useMemo, useState } from '@wordpress/element';
import useConnection from '../../hooks/useConnection';

export const useIsFullyConnected = () => {
const [ connectionStatus ] = useConnection();
const connectionStatus = useConnection();

return useMemo( () => {
const connectionLoaded = 0 < Object.keys( connectionStatus ).length;
Expand All @@ -13,7 +13,7 @@ export const useIsFullyConnected = () => {

export const useIsSecondaryAdminNotConnected = () => {
const isFullyConnected = useIsFullyConnected();
const [ connectionStatus ] = useConnection();
const connectionStatus = useConnection();

return useMemo( () => {
return isFullyConnected && ! connectionStatus.isUserConnected;
Expand Down
8 changes: 4 additions & 4 deletions projects/packages/backup/src/js/components/Admin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import useBackupsState from '../../hooks/useBackupsState';
import useCapabilities from '../../hooks/useCapabilities';
import useConnection from '../../hooks/useConnection';
import { Backups, Loading as BackupsLoadingPlaceholder } from '../Backups';
import { BackupConnectionScreen } from '../backup-connection-screen';
import { BackupSecondaryAdminConnectionScreen } from '../backup-connection-screen/secondary-admin';
import BackupStorageSpace from '../backup-storage-space';
import ReviewRequest from '../review-request';
import Header from './header';
Expand All @@ -31,7 +33,7 @@ import '../masthead/masthead-style.scss';

/* eslint react/react-in-jsx-scope: 0 */
const Admin = () => {
const [ connectionStatus, , BackupSecondaryAdminConnectionScreen ] = useConnection();
const connectionStatus = useConnection();
const { tracks } = useAnalytics();
const { hasConnectionError } = useConnectionErrorNotice();
const connectionLoaded = 0 < Object.keys( connectionStatus ).length;
Expand Down Expand Up @@ -119,7 +121,7 @@ const Admin = () => {
// Render additional segments for the backup admin page under the jp-hero section.
// If the user has a backup plan and is connected, we render the storage space segment.
const BackupSegments = ( { hasBackupPlan, connectionLoaded } ) => {
const [ connectionStatus ] = useConnection();
const connectionStatus = useConnection();
const { tracks } = useAnalytics();

const trackLearnMoreClick = useCallback( () => {
Expand Down Expand Up @@ -337,8 +339,6 @@ const LoadedState = ( {
capabilities,
isFullyConnected,
} ) => {
const [ , BackupConnectionScreen ] = useConnection();

if ( ! isFullyConnected ) {
return (
<Container horizontalSpacing={ 3 } horizontalGap={ 3 }>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import useConnection from '../../hooks/useConnection';
import { useIsFullyConnected } from '../Admin/hooks';

export const useShowBackUpNow = () => {
const [ connectionStatus ] = useConnection();
const connectionStatus = useConnection();
const isFullyConnected = useIsFullyConnected();
const { capabilitiesLoaded, hasBackupPlan } = useCapabilities();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { JetpackVaultPressBackupLogo, Testimonials } from '@automattic/jetpack-components';
import { ConnectScreenRequiredPlan } from '@automattic/jetpack-connection';
import apiFetch from '@wordpress/api-fetch';
import { useSelect } from '@wordpress/data';
import { useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import React from 'react';
import { useBackupProductInfo } from '../../hooks/use-backup-product-info';
import { STORE_ID } from '../../store';
import BackupPromotionBlock from '../backup-promotion';
import { BackupVideoSection } from '../backup-video-section';
import { WhyINeedVPBackup } from '../why-i-need-vp-backup';
import benGiordanoTestimonial from './assets/ben-giordano-testimonial.png';
import timFerrissTestimonial from './assets/tim-ferriss-testimonial.png';

const testimonials = [
{
quote: __(
'Millions of people depend on my site, and downtime isn’t an option. Jetpack VaultPress Backup handles my site security and backups so I can focus on creation.',
'jetpack-backup-pkg'
),
author: 'Tim Ferriss',
profession: __( 'Author / Investor / Podcaster', 'jetpack-backup-pkg' ),
img: timFerrissTestimonial,
},
{
quote: __(
'Our developers use VaultPress Backup all the time. It’s a one‑click way to return to where we were before things got wonky. It gives us a little emergency parachute so if we’re working on a customization that breaks everything, we lose minutes, not hours.',
'jetpack-backup-pkg'
),
author: 'Ben Giordano',
profession: __( 'Founder, FreshySites.com', 'jetpack-backup-pkg' ),

img: benGiordanoTestimonial,
},
];

export const BackupConnectionScreen = () => {
const APINonce = useSelect( select => select( STORE_ID ).getAPINonce(), [] );
const APIRoot = useSelect( select => select( STORE_ID ).getAPIRoot(), [] );
const registrationNonce = useSelect( select => select( STORE_ID ).getRegistrationNonce(), [] );
const { price, priceAfter } = useBackupProductInfo();

const checkSiteHasBackupProduct = useCallback(
() => apiFetch( { path: '/jetpack/v4/has-backup-plan' } ),
[]
);

return (
<>
<ConnectScreenRequiredPlan
buttonLabel={ __( 'Get VaultPress Backup', 'jetpack-backup-pkg' ) }
priceAfter={ priceAfter }
priceBefore={ price }
pricingIcon={ <JetpackVaultPressBackupLogo showText={ false } /> }
pricingTitle={ __( 'VaultPress Backup', 'jetpack-backup-pkg' ) }
title={ __( 'The best real-time WordPress backups', 'jetpack-backup-pkg' ) }
apiRoot={ APIRoot }
apiNonce={ APINonce }
registrationNonce={ registrationNonce }
from="jetpack-backup"
redirectUri="admin.php?page=jetpack-backup"
wpcomProductSlug="jetpack_backup_t1_yearly"
siteProductAvailabilityHandler={ checkSiteHasBackupProduct }
logo={ <></> }
rna
>
<BackupPromotionBlock />
</ConnectScreenRequiredPlan>

<Testimonials testimonials={ testimonials } />

<BackupVideoSection
registrationNonce={ registrationNonce }
apiRoot={ APIRoot }
apiNonce={ APINonce }
siteProductAvailabilityHandler={ checkSiteHasBackupProduct }
/>

<WhyINeedVPBackup />
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { JetpackVaultPressBackupLogo } from '@automattic/jetpack-components';
import { ConnectScreen } from '@automattic/jetpack-connection';
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import React from 'react';
import { STORE_ID } from '../../store';
import connectImage from './assets/connect-backup.png';

export const BackupSecondaryAdminConnectionScreen = () => {
const APINonce = useSelect( select => select( STORE_ID ).getAPINonce(), [] );
const APIRoot = useSelect( select => select( STORE_ID ).getAPIRoot(), [] );
const registrationNonce = useSelect( select => select( STORE_ID ).getRegistrationNonce(), [] );

return (
<ConnectScreen
title={ __( 'Save every change and get back online quickly', 'jetpack-backup-pkg' ) }
buttonLabel={ __( 'Log in to continue', 'jetpack-backup-pkg' ) }
apiRoot={ APIRoot }
apiNonce={ APINonce }
registrationNonce={ registrationNonce }
images={ [ connectImage ] }
from="jetpack-backup"
redirectUri="admin.php?page=jetpack-backup"
logo={ <JetpackVaultPressBackupLogo /> }
>
<p>
It looks like your site already has a backup plan activated. All you need to do is log in
with your WordPress account.
</p>
</ConnectScreen>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import StorageUsageDetails from './storage-usage-details';
import { getUsageLevel, StorageUsageLevels } from './storage-usage-levels';

const BackupStorageSpace = () => {
const [ connectionStatus ] = useConnection();
const connectionStatus = useConnection();
const isFetchingPolicies = useSelect( select => select( STORE_ID ).isFetchingBackupPolicies() );
const isFetchingSize = useSelect( select => select( STORE_ID ).isFetchingBackupSize() );
const hasBackupSizeLoaded = useSelect( select => select( STORE_ID ).hasBackupSizeLoaded() );
Expand Down
29 changes: 29 additions & 0 deletions projects/packages/backup/src/js/hooks/use-backup-product-info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import apiFetch from '@wordpress/api-fetch';
import { useState, useEffect, useCallback } from '@wordpress/element';

/**
* Custom hook to fetch and manage backup product pricing information.
*
* @return {object} An object containing the current product price and the price after any introductory offer.
*/
export function useBackupProductInfo() {
const [ price, setPrice ] = useState( 0 );
const [ priceAfter, setPriceAfter ] = useState( 0 );

const fetchBackupProductInfo = useCallback( () => {
return apiFetch( { path: '/jetpack/v4/backup-promoted-product-info' } );
}, [] );

useEffect( () => {
fetchBackupProductInfo().then( res => {
setPrice( res.cost / 12 );
if ( res.introductory_offer ) {
setPriceAfter( res.introductory_offer.cost_per_interval / 12 );
} else {
setPriceAfter( res.cost / 12 );
}
} );
}, [ fetchBackupProductInfo ] );

return { price, priceAfter };
}
2 changes: 1 addition & 1 deletion projects/packages/backup/src/js/hooks/useCapabilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function useCapabilities() {
const [ capabilities, setCapabilities ] = useState( null );
const [ capabilitiesError, setCapabilitiesError ] = useState( null );
const [ capabilitiesLoaded, setCapabilitiesLoaded ] = useState( false );
const [ connectionStatus ] = useConnection();
const connectionStatus = useConnection();

useEffect( () => {
const connectionLoaded = 0 < Object.keys( connectionStatus ).length;
Expand Down
125 changes: 4 additions & 121 deletions projects/packages/backup/src/js/hooks/useConnection.js
Original file line number Diff line number Diff line change
@@ -1,133 +1,16 @@
import { JetpackVaultPressBackupLogo, Testimonials } from '@automattic/jetpack-components';
import {
ConnectScreenRequiredPlan,
ConnectScreen,
CONNECTION_STORE_ID,
} from '@automattic/jetpack-connection';
import apiFetch from '@wordpress/api-fetch';
import { CONNECTION_STORE_ID } from '@automattic/jetpack-connection';
import { useSelect } from '@wordpress/data';
import { useState, useEffect } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import React, { useCallback } from 'react';
import BackupPromotionBlock from '../components/backup-promotion';
import { BackupVideoSection } from '../components/backup-video-section';
import { WhyINeedVPBackup } from '../components/why-i-need-vp-backup';
import { STORE_ID } from '../store';
import benGiordanoTestimonial from './assets/ben-giordano-testimonial.png';
import connectImage from './assets/connect-backup.png';
import timFerrissTestimonial from './assets/tim-ferriss-testimonial.png';

const testimonials = [
{
quote: __(
'Millions of people depend on my site, and downtime isn’t an option. Jetpack VaultPress Backup handles my site security and backups so I can focus on creation.',
'jetpack-backup-pkg'
),
author: 'Tim Ferriss',
profession: __( 'Author / Investor / Podcaster', 'jetpack-backup-pkg' ),
img: timFerrissTestimonial,
},
{
quote: __(
'Our developers use VaultPress Backup all the time. It’s a one‑click way to return to where we were before things got wonky. It gives us a little emergency parachute so if we’re working on a customization that breaks everything, we lose minutes, not hours.',
'jetpack-backup-pkg'
),
author: 'Ben Giordano',
profession: __( 'Founder, FreshySites.com', 'jetpack-backup-pkg' ),

img: benGiordanoTestimonial,
},
];

/**
* Expose the `connectionStatus` state object and `BackupConnectionScreen` to show a component used for connection.
* Expose the `connectionStatus` state object from the Jetpack connection store.
*
* @return {Array} connectionStatus, BackupConnectionScreen
* @return {object} connectionStatus The connection status object.
*/
export default function useConnection() {
const APINonce = useSelect( select => select( STORE_ID ).getAPINonce(), [] );
const APIRoot = useSelect( select => select( STORE_ID ).getAPIRoot(), [] );
const registrationNonce = useSelect( select => select( STORE_ID ).getRegistrationNonce(), [] );
const connectionStatus = useSelect(
select => select( CONNECTION_STORE_ID ).getConnectionStatus(),
[]
);
const [ price, setPrice ] = useState( 0 );
const [ priceAfter, setPriceAfter ] = useState( 0 );

const checkSiteHasBackupProduct = useCallback(
() => apiFetch( { path: '/jetpack/v4/has-backup-plan' } ),
[]
);

useEffect( () => {
apiFetch( { path: '/jetpack/v4/backup-promoted-product-info' } ).then( res => {
setPrice( res.cost / 12 );
if ( res.introductory_offer ) {
setPriceAfter( res.introductory_offer.cost_per_interval / 12 );
} else {
setPriceAfter( res.cost / 12 );
}
} );
}, [] );

const BackupConnectionScreen = () => {
return (
<>
<ConnectScreenRequiredPlan
buttonLabel={ __( 'Get VaultPress Backup', 'jetpack-backup-pkg' ) }
priceAfter={ priceAfter }
priceBefore={ price }
pricingIcon={ <JetpackVaultPressBackupLogo showText={ false } /> }
pricingTitle={ __( 'VaultPress Backup', 'jetpack-backup-pkg' ) }
title={ __( 'The best real-time WordPress backups', 'jetpack-backup-pkg' ) }
apiRoot={ APIRoot }
apiNonce={ APINonce }
registrationNonce={ registrationNonce }
from="jetpack-backup"
redirectUri="admin.php?page=jetpack-backup"
wpcomProductSlug="jetpack_backup_t1_yearly"
siteProductAvailabilityHandler={ checkSiteHasBackupProduct }
logo={ <></> }
rna
>
<BackupPromotionBlock />
</ConnectScreenRequiredPlan>

<Testimonials testimonials={ testimonials } />

<BackupVideoSection
registrationNonce={ registrationNonce }
apiRoot={ APIRoot }
apiNonce={ APINonce }
siteProductAvailabilityHandler={ checkSiteHasBackupProduct }
/>

<WhyINeedVPBackup />
</>
);
};

const BackupSecondaryAdminConnectionScreen = () => {
return (
<ConnectScreen
title={ __( 'Save every change and get back online quickly', 'jetpack-backup-pkg' ) }
buttonLabel={ __( 'Log in to continue', 'jetpack-backup-pkg' ) }
apiRoot={ APIRoot }
apiNonce={ APINonce }
registrationNonce={ registrationNonce }
images={ [ connectImage ] }
from="jetpack-backup"
redirectUri="admin.php?page=jetpack-backup"
logo={ <JetpackVaultPressBackupLogo /> }
>
<p>
It looks like your site already has a backup plan activated. All you need to do is log in
with your WordPress account.
</p>
</ConnectScreen>
);
};

return [ connectionStatus, BackupConnectionScreen, BackupSecondaryAdminConnectionScreen ];
return connectionStatus;
}

0 comments on commit a950c3f

Please sign in to comment.