Skip to content

Commit

Permalink
AI Logo Generator: fix the "Use on site" button (#38552)
Browse files Browse the repository at this point in the history
* Change label of button to refer to the block

* Do not call the hook's applyLogo and rely only on the callback provided

* Support an external onApplyLogo handler on the generator modal

* Change the post-use screen to not reload the editor

* Link to the proper Jetpack redirect link on the learn more button

* Fix message to avoid misleading user about the result of the action

* Provide the saved media ID on the apply logo handler and make it required

* Use the media ID to update the site logo and icon

* changelog

* Changelog

* Remove time interval request check to prevent errors when multiple blocks are present on the editor
  • Loading branch information
lhkowalski authored Jul 26, 2024
1 parent ea03b63 commit 13e664f
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 66 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fixed

AI Logo Generator: provide the saved media ID on the save logo callback.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const debug = debugFactory( 'jetpack-ai-calypso:generator-modal' );
export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
isOpen,
onClose,
onApplyLogo,
siteDetails,
context,
} ) => {
Expand All @@ -62,7 +63,6 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
useLogoGenerator();
const { featureFetchError, firstLogoPromptFetchError, clearErrors } = useRequestErrors();
const siteId = siteDetails?.ID;
const siteURL = siteDetails?.URL;
const [ logoAccepted, setLogoAccepted ] = useState( false );

// First fetch the feature data so we have the most up-to-date info from the backend.
Expand Down Expand Up @@ -165,17 +165,9 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
recordTracksEvent( EVENT_MODAL_CLOSE, { context, placement: EVENT_PLACEMENT_QUICK_LINKS } );
};

const handleApplyLogo = () => {
const handleApplyLogo = ( mediaId: number ) => {
setLogoAccepted( true );
};

const handleCloseAndReload = () => {
closeModal();

setTimeout( () => {
// Reload the page to update the logo.
window.location.reload();
}, 1000 );
onApplyLogo?.( mediaId );
};

const handleFeedbackClick = () => {
Expand Down Expand Up @@ -235,13 +227,10 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
/>
{ logoAccepted ? (
<div className="jetpack-ai-logo-generator__accept">
<VisitSiteBanner siteURL={ siteURL } onVisitBlankTarget={ handleCloseAndReload } />
<VisitSiteBanner onVisitBlankTarget={ closeModal } />
<div className="jetpack-ai-logo-generator__accept-actions">
<Button variant="link" onClick={ handleCloseAndReload }>
{ __( 'Close and refresh', 'jetpack-ai-client' ) }
</Button>
<Button href={ siteURL } variant="primary">
{ __( 'Visit site', 'jetpack-ai-client' ) }
<Button variant="primary" onClick={ closeModal }>
{ __( 'Close', 'jetpack-ai-client' ) }
</Button>
</div>
</div>
Expand Down Expand Up @@ -271,7 +260,7 @@ export const GeneratorModal: React.FC< GeneratorModalProps > = ( {
{ isOpen && (
<Modal
className="jetpack-ai-logo-generator-modal"
onRequestClose={ logoAccepted ? handleCloseAndReload : closeModal }
onRequestClose={ closeModal }
shouldCloseOnClickOutside={ false }
shouldCloseOnEsc={ false }
title={ __( 'Jetpack AI Logo Generator', 'jetpack-ai-client' ) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,49 +86,34 @@ const SaveInLibraryButton: React.FC< { siteId: string } > = ( { siteId } ) => {
);
};

const UseOnSiteButton: React.FC< { onApplyLogo: () => void } > = ( { onApplyLogo } ) => {
const UseOnSiteButton: React.FC< { onApplyLogo: ( mediaId: number ) => void } > = ( {
onApplyLogo,
} ) => {
const { tracks } = useAnalytics();
const { recordEvent: recordTracksEvent } = tracks;
const {
applyLogo,
isSavingLogoToLibrary,
isApplyingLogo,
selectedLogo,
logos,
selectedLogoIndex,
context,
} = useLogoGenerator();
const { isSavingLogoToLibrary, selectedLogo, logos, selectedLogoIndex, context } =
useLogoGenerator();

const handleClick = async () => {
if ( ! isApplyingLogo && ! isSavingLogoToLibrary ) {
if ( ! isSavingLogoToLibrary ) {
recordTracksEvent( EVENT_USE, {
context,
logos_count: logos.length,
selected_logo: selectedLogoIndex != null ? selectedLogoIndex + 1 : 0,
} );

try {
await applyLogo();
onApplyLogo();
} catch ( error ) {
debug( 'Error applying logo', error );
}
onApplyLogo?.( selectedLogo?.mediaId );
}
};

return isApplyingLogo && ! isSavingLogoToLibrary ? (
<button className="jetpack-ai-logo-generator-modal-presenter__action">
<Icon icon={ <LogoIcon /> } />
<span className="action-text">{ __( 'Applying logo…', 'jetpack-ai-client' ) }</span>
</button>
) : (
return (
<Button
className="jetpack-ai-logo-generator-modal-presenter__action"
onClick={ handleClick }
disabled={ isSavingLogoToLibrary || ! selectedLogo?.mediaId }
>
<Icon icon={ <LogoIcon /> } />
<span className="action-text">{ __( 'Use on Site', 'jetpack-ai-client' ) }</span>
<span className="action-text">{ __( 'Use on block', 'jetpack-ai-client' ) }</span>
</Button>
);
};
Expand All @@ -144,11 +129,11 @@ const LogoLoading: React.FC = () => {
);
};

const LogoReady: React.FC< { siteId: string; logo: Logo; onApplyLogo: () => void } > = ( {
siteId,
logo,
onApplyLogo,
} ) => {
const LogoReady: React.FC< {
siteId: string;
logo: Logo;
onApplyLogo: ( mediaId: number ) => void;
} > = ( { siteId, logo, onApplyLogo } ) => {
return (
<>
<img
Expand Down Expand Up @@ -179,7 +164,7 @@ const LogoUpdated: React.FC< { logo: Logo } > = ( { logo } ) => {
/>
<div className="jetpack-ai-logo-generator-modal-presenter__success-wrapper">
<Icon icon={ <CheckIcon /> } />
<span>{ __( 'Your logo has been successfully updated!', 'jetpack-ai-client' ) }</span>
<span>{ __( 'Your new logo was set to the block!', 'jetpack-ai-client' ) }</span>
</div>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ import type React from 'react';

export const VisitSiteBanner: React.FC< {
className?: string;
siteURL?: string;
onVisitBlankTarget: () => void;
} > = ( { className = null, siteURL = '#', onVisitBlankTarget } ) => {
} > = ( { className = null, onVisitBlankTarget } ) => {
return (
<div className={ clsx( 'jetpack-ai-logo-generator-modal-visit-site-banner', className ) }>
<div className="jetpack-ai-logo-generator-modal-visit-site-banner__jetpack-logo">
Expand All @@ -39,8 +38,13 @@ export const VisitSiteBanner: React.FC< {
) }
</span>
<div>
<Button variant="link" href={ siteURL } target="_blank" onClick={ onVisitBlankTarget }>
{ __( 'Visit website', 'jetpack-ai-client' ) }
<Button
variant="link"
href="https://jetpack.com/redirect/?source=logo_generator_learn_more_about_jetpack_ai"
target="_blank"
onClick={ onVisitBlankTarget }
>
{ __( 'Learn more about Jetpack AI', 'jetpack-ai-client' ) }
<Icon icon={ external } size={ 20 } />
</Button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import apiFetch from '../../api-fetch/index.js';
const MAX_CONCURRENT_REQUESTS = 5;

let concurrentCounter = 0;
let lastCallTimestamp: number | null = null;

/**
* Concurrency-limited request to wpcom-proxy-request.
Expand All @@ -25,16 +24,6 @@ export default async function wpcomLimitedRequest< T >( params: object ): Promis
throw new Error( 'Too many requests' );
}

const now = Date.now();

// Check if the last call was made less than 100 milliseconds ago
if ( lastCallTimestamp && now - lastCallTimestamp < 100 ) {
concurrentCounter -= 1;
throw new Error( 'Too many requests' );
}

lastCallTimestamp = now; // Update the timestamp

return apiFetch< T >( params ).finally( () => {
concurrentCounter -= 1;
} );
Expand Down
3 changes: 2 additions & 1 deletion projects/js-packages/ai-client/src/logo-generator/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ export interface GeneratorModalProps {
siteDetails?: SiteDetails;
isOpen: boolean;
onClose: () => void;
onApplyLogo: ( mediaId: number ) => void;
context: string;
}

export interface LogoPresenterProps {
logo?: Logo;
loading?: boolean;
onApplyLogo: () => void;
onApplyLogo: ( mediaId: number ) => void;
logoAccepted?: boolean;
siteId: string | number;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: other

AI Logo Generator: support saving the logo and the icon when an image is ready.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { GeneratorModal } from '@automattic/jetpack-ai-client';
import { BlockControls } from '@wordpress/block-editor';
import { createHigherOrderComponent } from '@wordpress/compose';
import { useSelect } from '@wordpress/data';
import { useDispatch, useSelect } from '@wordpress/data';
import { useCallback, useEffect, useState } from '@wordpress/element';
import { addFilter } from '@wordpress/hooks';
/*
Expand All @@ -28,6 +28,46 @@ type CoreSelect = {
};
};

/**
* Hook to set the site logo on the local state, affecting the logo block.
*
* @returns {object} An object with the setLogo function.
*/
const useSetLogo = () => {
const editEntityRecord = useDispatch( 'core' ).editEntityRecord;
const saveLogo = useCallback(
( mediaId: number ) => {
editEntityRecord( 'root', 'site', undefined, {
site_logo: mediaId,
} );
},
[ editEntityRecord ]
);

const saveIcon = useCallback(
( mediaId: number ) => {
editEntityRecord( 'root', 'site', undefined, {
site_icon: mediaId,
} );
},
[ editEntityRecord ]
);

const setLogo = useCallback(
( mediaId: number, updateIcon: boolean ) => {
saveLogo( mediaId );
if ( updateIcon ) {
saveIcon( mediaId );
}
},
[ saveLogo, saveIcon ]
);

return {
setLogo,
};
};

const useSiteDetails = () => {
const siteSettings = useSelect( select => {
return ( select( 'core' ) as CoreSelect ).getEntityRecord( 'root', 'site' );
Expand All @@ -48,6 +88,8 @@ const useSiteDetails = () => {
const siteLogoEditWithAiComponents = createHigherOrderComponent( BlockEdit => {
return props => {
const [ isLogoGeneratorModalVisible, setIsLogoGeneratorModalVisible ] = useState( false );
const { setLogo } = useSetLogo();
const shouldSyncIcon = props?.attributes?.shouldSyncIcon || false;

const showModal = useCallback( () => {
setIsLogoGeneratorModalVisible( true );
Expand All @@ -57,9 +99,18 @@ const siteLogoEditWithAiComponents = createHigherOrderComponent( BlockEdit => {
setIsLogoGeneratorModalVisible( false );
}, [] );

const applyLogoHandler = useCallback(
( mediaId: number ) => {
if ( mediaId ) {
setLogo( mediaId, shouldSyncIcon );
}
},
[ setLogo, shouldSyncIcon ]
);

useEffect( () => {
return () => {
// close modal if open
// close modal if open when the component unmounts
closeModal();
};
}, [ closeModal ] );
Expand All @@ -75,6 +126,7 @@ const siteLogoEditWithAiComponents = createHigherOrderComponent( BlockEdit => {
<GeneratorModal
isOpen={ isLogoGeneratorModalVisible }
onClose={ closeModal }
onApplyLogo={ applyLogoHandler }
context="block-editor"
siteDetails={ siteDetails }
/>
Expand Down

0 comments on commit 13e664f

Please sign in to comment.