diff --git a/packages/js/src/ai-consent/components/revoke-consent.js b/packages/js/src/ai-consent/components/revoke-consent.js index a6e77330cae..140022c5f1c 100644 --- a/packages/js/src/ai-consent/components/revoke-consent.js +++ b/packages/js/src/ai-consent/components/revoke-consent.js @@ -14,26 +14,22 @@ import { STORE_NAME_AI_CONSENT } from "../constants"; * @returns {JSX.Element} The element. */ export const RevokeConsent = ( { onClose } ) => { - const { storeAiGeneratorConsent } = useDispatch( STORE_NAME_AI_CONSENT ); + const { storeAiGeneratorConsent, giveAiGeneratorConsent } = useDispatch( STORE_NAME_AI_CONSENT ); const endpoint = useSelect( select => select( STORE_NAME_AI_CONSENT ).selectAiGeneratorConsentEndpoint(), [] ); const [ isLoading, setIsLoading ] = useState( false ); - const [ error, setError ] = useState( false ); const handleRevokeConsent = useCallback( async() => { - setError( false ); setIsLoading( true ); - const response = await storeAiGeneratorConsent( false, endpoint ); - if ( response.consent === false ) { - setError( true ); - setIsLoading( false ); - return; - } + await storeAiGeneratorConsent( false, endpoint ); + // The backend always revokes consent locally, even when the remote call fails, so the + // local store must reflect that regardless of the request's outcome. + giveAiGeneratorConsent( false ); onClose(); setIsLoading( false ); - }, [ storeAiGeneratorConsent, setIsLoading, onClose, endpoint ] ); + }, [ storeAiGeneratorConsent, giveAiGeneratorConsent, setIsLoading, onClose, endpoint ] ); return (
@@ -52,12 +48,6 @@ export const RevokeConsent = ( { onClose } ) => { > { __( "Revoke AI consent", "wordpress-seo" ) } - { error && - { __( "Something went wrong, please try again later.", "wordpress-seo" ) } - }

{ } { __( "By revoking your consent, you will no longer have access to Yoast AI features. Are you sure you want to revoke your consent?", "wordpress-seo" ) } diff --git a/packages/js/src/ai-consent/initialize.js b/packages/js/src/ai-consent/initialize.js index ffd64adbc77..93d4bd6a83e 100644 --- a/packages/js/src/ai-consent/initialize.js +++ b/packages/js/src/ai-consent/initialize.js @@ -5,7 +5,7 @@ import { __ } from "@wordpress/i18n"; import { Modal, useToggleState } from "@yoast/ui-library"; import classNames from "classnames"; import { get } from "lodash"; -import { HAS_AI_GENERATOR_CONSENT_NAME, PLUGIN_URL_NAME, LINK_PARAMS_NAME } from "../shared-admin/store"; +import { ADMIN_URL_NAME, HAS_AI_GENERATOR_CONSENT_NAME, PLUGIN_URL_NAME, LINK_PARAMS_NAME } from "../shared-admin/store"; import { GrantConsent } from "./components/grant-consent"; import { RevokeConsent } from "./components/revoke-consent"; import { STORE_NAME_AI_CONSENT } from "./constants"; @@ -19,6 +19,7 @@ domReady( () => { }, [ PLUGIN_URL_NAME ]: get( window, "wpseoAiConsent.pluginUrl", "" ), [ LINK_PARAMS_NAME ]: get( window, "wpseoAiConsent.linkParams", {} ), + [ ADMIN_URL_NAME ]: get( window, "wpseoAiConsent.adminUrl", "" ), } ); /** diff --git a/packages/js/src/ai-consent/store/index.js b/packages/js/src/ai-consent/store/index.js index 735aae4382a..f39692cde85 100644 --- a/packages/js/src/ai-consent/store/index.js +++ b/packages/js/src/ai-consent/store/index.js @@ -1,6 +1,11 @@ import { combineReducers, createReduxStore, register } from "@wordpress/data"; import { merge } from "lodash"; import { + ADMIN_URL_NAME, + adminUrlActions, + adminUrlReducer, + adminUrlSelectors, + getInitialAdminUrlState, getInitialHasAiGeneratorConsentState, getInitialLinkParamsState, getInitialPluginUrlState, @@ -32,11 +37,13 @@ const createStore = ( initialState ) => { ...hasAiGeneratorConsentActions, ...pluginUrlActions, ...linkParamsActions, + ...adminUrlActions, }, selectors: { ...hasAiGeneratorConsentSelectors, ...pluginUrlSelectors, ...linkParamsSelectors, + ...adminUrlSelectors, }, initialState: merge( {}, @@ -44,6 +51,7 @@ const createStore = ( initialState ) => { [ HAS_AI_GENERATOR_CONSENT_NAME ]: getInitialHasAiGeneratorConsentState(), [ PLUGIN_URL_NAME ]: getInitialPluginUrlState(), [ LINK_PARAMS_NAME ]: getInitialLinkParamsState(), + [ ADMIN_URL_NAME ]: getInitialAdminUrlState(), }, initialState ), @@ -51,6 +59,7 @@ const createStore = ( initialState ) => { [ HAS_AI_GENERATOR_CONSENT_NAME ]: hasAiGeneratorConsentReducer, [ PLUGIN_URL_NAME ]: pluginUrlReducer, [ LINK_PARAMS_NAME ]: linkParamsReducer, + [ ADMIN_URL_NAME ]: adminUrlReducer, } ), controls: { ...hasAiGeneratorConsentControls, diff --git a/packages/js/src/ai-generator/components/errors/generic-alert.js b/packages/js/src/ai-generator/components/errors/generic-alert.js index f687ab4c0c3..67619cbe23e 100644 --- a/packages/js/src/ai-generator/components/errors/generic-alert.js +++ b/packages/js/src/ai-generator/components/errors/generic-alert.js @@ -1,16 +1,22 @@ import { useSelect } from "@wordpress/data"; import { __, sprintf } from "@wordpress/i18n"; import { Alert } from "@yoast/ui-library"; +import PropTypes from "prop-types"; import { safeCreateInterpolateElement } from "../../../helpers/i18n"; import { OutboundLink } from "../../../shared-admin/components"; import { STORE_NAME_EDITOR } from "../../constants"; /** + * @param {string} [linkStoreName] The store to read the common-errors and support links from. + * Defaults to the block editor's store; pass a different store + * name when rendering outside the block editor (e.g. the AI + * consent screen on the user profile page). + * * @returns {JSX.Element} The element. */ -export const GenericAlert = () => { - const commonErrorsLink = useSelect( select => select( STORE_NAME_EDITOR ).selectLink( "https://yoa.st/ai-common-errors" ), [] ); - const supportLink = useSelect( select => select( STORE_NAME_EDITOR ).selectAdminLink( "?page=wpseo_page_support" ), [] ); +export const GenericAlert = ( { linkStoreName = STORE_NAME_EDITOR } ) => { + const commonErrorsLink = useSelect( select => select( linkStoreName ).selectLink( "https://yoa.st/ai-common-errors" ), [ linkStoreName ] ); + const supportLink = useSelect( select => select( linkStoreName ).selectAdminLink( "?page=wpseo_page_support" ), [ linkStoreName ] ); return ( @@ -35,3 +41,6 @@ export const GenericAlert = () => { ); }; +GenericAlert.propTypes = { + linkStoreName: PropTypes.string, +}; diff --git a/packages/js/src/shared-admin/components/ai-consent.js b/packages/js/src/shared-admin/components/ai-consent.js index dcd2843980f..f7f411d310b 100644 --- a/packages/js/src/shared-admin/components/ai-consent.js +++ b/packages/js/src/shared-admin/components/ai-consent.js @@ -1,6 +1,7 @@ import ArrowNarrowRightIcon from "@heroicons/react/solid/ArrowNarrowRightIcon"; -import { useMemo, useCallback } from "@wordpress/element"; +import { useMemo, useCallback, useState } from "@wordpress/element"; import { __, sprintf } from "@wordpress/i18n"; +import { GenericAlert } from "../../ai-generator/components/errors"; import { Button, useModalContext, useToggleState, Spinner } from "@yoast/ui-library"; import PropTypes from "prop-types"; import { OutboundLink } from "."; @@ -14,6 +15,7 @@ import { safeCreateInterpolateElement } from "../../helpers/i18n"; * @param {string} privacyPolicyLink The privacy policy link. * @param {string} termsOfServiceLink The terms of service link. * @param {Object} imageLink The thumbnail: img props. + * @param {string} linkStoreName The store to read the error alert's links from. * * @returns {JSX.Element} The element. */ @@ -23,9 +25,11 @@ export const AiConsent = ( { privacyPolicyLink, termsOfServiceLink, imageLink, + linkStoreName, } ) => { const { onClose, initialFocus } = useModalContext(); const [ consent, toggleConsent ] = useToggleState( false ); + const [ error, setError ] = useState( false ); const thumbnail = useMemo( () => ( { src: imageLink, @@ -55,7 +59,10 @@ export const AiConsent = ( { const [ loading, toggleLoading ] = useToggleState( false ); const handleConsentChange = useCallback( async() => { toggleLoading(); - await onGiveConsent(); + + const response = await onGiveConsent(); + setError( response === false ); + toggleLoading(); }, [ onGiveConsent ] ); @@ -128,15 +135,18 @@ export const AiConsent = ( { { checkboxLabel }

+ { error && + + }