Skip to content

Commit

Permalink
Merge pull request #9206 from google/enhancement/#8165-audience-creat…
Browse files Browse the repository at this point in the history
…ion-oauth-flow

Enhancement/#8165 - Implement the Audience Creation Notice OAuth flow
  • Loading branch information
techanvil authored Aug 21, 2024
2 parents e9fd768 + d3e985b commit 7c1e93d
Show file tree
Hide file tree
Showing 24 changed files with 663 additions and 33 deletions.
22 changes: 13 additions & 9 deletions assets/js/components/notifications/SubtleNotification.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ function SubtleNotification( {
dismissLabel = __( 'Ok, got it', 'google-site-kit' ),
onDismiss,
variant = VARIANTS.SUCCESS,
hideIcon = false,
} ) {
return (
<Grid>
Expand All @@ -69,15 +70,17 @@ function SubtleNotification( {
}
) }
>
<div className="googlesitekit-subtle-notification__icon">
{ Icon && <Icon width={ 24 } height={ 24 } /> }
{ ! Icon && variant === VARIANTS.SUCCESS && (
<CheckFillSVG width={ 24 } height={ 24 } />
) }
{ ! Icon && variant === VARIANTS.WARNING && (
<WarningSVG width={ 24 } height={ 24 } />
) }
</div>
{ ! hideIcon && (
<div className="googlesitekit-subtle-notification__icon">
{ Icon && <Icon width={ 24 } height={ 24 } /> }
{ ! Icon && variant === VARIANTS.SUCCESS && (
<CheckFillSVG width={ 24 } height={ 24 } />
) }
{ ! Icon && variant === VARIANTS.WARNING && (
<WarningSVG width={ 24 } height={ 24 } />
) }
</div>
) }
<div className="googlesitekit-subtle-notification__content">
<p>{ title }</p>
{ description && (
Expand Down Expand Up @@ -128,6 +131,7 @@ SubtleNotification.propTypes = {
dismissLabel: PropTypes.string,
onDismiss: PropTypes.func.isRequired,
variant: PropTypes.oneOf( Object.values( VARIANTS ) ),
hideIcon: PropTypes.bool,
};

export default SubtleNotification;
25 changes: 25 additions & 0 deletions assets/js/components/notifications/SubtleNotification.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,18 @@ SuccessWithCustomIcon.args = {
};
SuccessWithCustomIcon.scenario = {};

export const SuccessWithoutIcon = Template.bind( {} );
SuccessWithoutIcon.storyName = 'Success Without Icon';
SuccessWithoutIcon.args = {
title: 'Success! Your Conversion Tracking ID was added to your site',
dismissLabel: 'Ok, got it',
ctaLabel: 'Learn more',
ctaLink: 'https://sitekit.withgoogle.com/documentation',
isCTALinkExternal: true,
hideIcon: true,
};
SuccessWithoutIcon.scenario = {};

export const Warning = Template.bind( {} );
Warning.storyName = 'Warning';
Warning.args = {
Expand Down Expand Up @@ -136,6 +148,19 @@ WarningWithCustomIcon.args = {
};
WarningWithCustomIcon.scenario = {};

export const WarningWithoutIcon = Template.bind( {} );
WarningWithoutIcon.storyName = 'Warning Without Icon';
WarningWithoutIcon.args = {
title: 'Warning! Your Conversion Tracking ID was added to your site',
dismissLabel: 'Ok, got it',
ctaLabel: 'Learn more',
ctaLink: 'https://sitekit.withgoogle.com/documentation',
isCTALinkExternal: true,
variant: 'warning',
hideIcon: true,
};
WarningWithoutIcon.scenario = {};

export default {
title: 'Components/SubtleNotification',
component: SubtleNotification,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,37 @@
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { useState } from '@wordpress/element';
import { useCallback, useEffect, useState } from '@wordpress/element';
import { addQueryArgs } from '@wordpress/url';

/**
* Internal dependencies
*/
import { useDispatch, useSelect } from 'googlesitekit-data';
import {
AUDIENCE_CREATION_EDIT_SCOPE_NOTICE_SLUG,
AUDIENCE_CREATION_FORM,
AUDIENCE_CREATION_NOTICE_SLUG,
AUDIENCE_CREATION_SUCCESS_NOTICE_SLUG,
AUDIENCE_SELECTION_PANEL_OPENED_KEY,
} from './constants';
import { useDispatch, useSelect } from 'googlesitekit-data';
import { CORE_FORMS } from '../../../../../../googlesitekit/datastore/forms/constants';
import { CORE_USER } from '../../../../../../googlesitekit/datastore/user/constants';
import { CORE_UI } from '../../../../../../googlesitekit/datastore/ui/constants';
import {
EDIT_SCOPE,
MODULES_ANALYTICS_4,
SITE_KIT_AUDIENCE_DEFINITIONS,
} from '../../../../datastore/constants';
import { ERROR_CODE_MISSING_REQUIRED_SCOPE } from '../../../../../../util/errors';
import Link from '../../../../../../components/Link';
import CloseIcon from '../../../../../../../svg/icons/close.svg';
import SpinnerButton, {
SPINNER_POSITION,
} from '../../../../../../googlesitekit/components-gm2/SpinnerButton';
import SubtleNotification, {
VARIANTS,
} from '../../../../../../components/notifications/SubtleNotification';

export default function AudienceCreationNotice() {
const [ isCreatingAudience, setIsCreatingAudience ] = useState( false );
Expand Down Expand Up @@ -69,30 +79,116 @@ export default function AudienceCreationNotice() {
const isItemDismissed = useSelect( ( select ) =>
select( CORE_USER ).isItemDismissed( AUDIENCE_CREATION_NOTICE_SLUG )
);
const isEditScopeNoticeDismissed = useSelect( ( select ) =>
select( CORE_USER ).isItemDismissed(
AUDIENCE_CREATION_EDIT_SCOPE_NOTICE_SLUG
)
);
const hasAnalytics4EditScope = useSelect( ( select ) =>
select( CORE_USER ).hasScope( EDIT_SCOPE )
);

const onCloseClick = () => {
dismissItem( AUDIENCE_CREATION_NOTICE_SLUG );
};

const redirectURL = addQueryArgs( global.location.href, {
notification: 'audience_segmentation',
} );

const { setValues } = useDispatch( CORE_FORMS );
const { setPermissionScopeError } = useDispatch( CORE_USER );
const { createAudience, syncAvailableAudiences } =
useDispatch( MODULES_ANALYTICS_4 );

const handleCreateAudience = async ( audienceSlug ) => {
setIsCreatingAudience( audienceSlug );
const isCreatingAudienceFromOAuth = useSelect( ( select ) =>
select( CORE_FORMS ).getValue( AUDIENCE_CREATION_FORM, 'autoSubmit' )
);

const { error } = await createAudience(
SITE_KIT_AUDIENCE_DEFINITIONS[ audienceSlug ]
);
const failedAudienceToCreate = useSelect( ( select ) =>
select( CORE_FORMS ).getValue(
AUDIENCE_CREATION_FORM,
'audienceToCreate'
)
);

await syncAvailableAudiences();
const handleCreateAudience = useCallback(
async ( audienceSlug ) => {
setIsCreatingAudience( audienceSlug );

setIsCreatingAudience( false );
if ( ! hasAnalytics4EditScope ) {
setValues( AUDIENCE_CREATION_FORM, {
autoSubmit: true,
audienceToCreate: audienceSlug,
} );

if ( ! error ) {
setValue( AUDIENCE_CREATION_SUCCESS_NOTICE_SLUG, true );
}
// Set permission scope error to trigger OAuth flow.
setPermissionScopeError( {
code: ERROR_CODE_MISSING_REQUIRED_SCOPE,
message: __(
'Additional permissions are required to create a new audience in Analytics.',
'google-site-kit'
),
data: {
status: 403,
scopes: [ EDIT_SCOPE ],
skipModal: true,
redirectURL,
},
} );

return;
}

setValues( AUDIENCE_CREATION_FORM, {
autoSubmit: false,
audienceToCreate: undefined,
} );

const { error } = await createAudience(
SITE_KIT_AUDIENCE_DEFINITIONS[ audienceSlug ]
);

await syncAvailableAudiences();

setIsCreatingAudience( false );

if ( ! error ) {
setValue( AUDIENCE_CREATION_SUCCESS_NOTICE_SLUG, true );
}
},
[
hasAnalytics4EditScope,
createAudience,
syncAvailableAudiences,
setValues,
setPermissionScopeError,
redirectURL,
setValue,
]
);

const handleDismissEditScopeNotice = () => {
dismissItem( AUDIENCE_CREATION_EDIT_SCOPE_NOTICE_SLUG );
};

useEffect( () => {
async function createAudienceFromOAuth() {
if ( hasAnalytics4EditScope && isCreatingAudienceFromOAuth ) {
setValue( AUDIENCE_SELECTION_PANEL_OPENED_KEY, true );
await handleCreateAudience( failedAudienceToCreate );
}
}

createAudienceFromOAuth();
}, [
failedAudienceToCreate,
handleCreateAudience,
hasAnalytics4EditScope,
isCreatingAudienceFromOAuth,
setValue,
] );

// Show the notice if the user has no site kit audiences, or has
// created one, and the user has not dismissed it.
const shouldShowNotice =
Expand Down Expand Up @@ -169,6 +265,20 @@ export default function AudienceCreationNotice() {
</div>
) ) }
</div>
{ ! hasAnalytics4EditScope && ! isEditScopeNoticeDismissed && (
<div className="googlesitekit-audience-selection-panel__audience-creation-notice-info">
<SubtleNotification
title={ __(
'Creating these groups require more data tracking. You will be directed to update your Analytics property.',
'google-site-kit'
) }
dismissLabel={ __( 'Got it', 'google-site-kit' ) }
onDismiss={ handleDismissEditScopeNotice }
variant={ VARIANTS.WARNING }
hideIcon
/>
</div>
) }
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@
/**
* Internal dependencies
*/
import { provideUserAuthentication } from '../../../../../../../../tests/js/utils';
import { CORE_USER } from '../../../../../../googlesitekit/datastore/user/constants';
import { MODULES_ANALYTICS_4 } from '../../../../datastore/constants';
import {
EDIT_SCOPE,
MODULES_ANALYTICS_4,
} from '../../../../datastore/constants';
import { availableAudiences } from '../../../../datastore/__fixtures__';
import WithRegistrySetup from '../../../../../../../../tests/js/WithRegistrySetup';
import AudienceCreationNotice from './AudienceCreationNotice';
Expand Down Expand Up @@ -61,12 +65,27 @@ WithOneAudience.args = {
},
};

export const WithMissingScopeNotice = Template.bind( {} );
WithMissingScopeNotice.storyName = 'WithMissingScopeNotice';
WithMissingScopeNotice.scenario = {};
WithMissingScopeNotice.args = {
setupRegistry: ( registry ) => {
provideUserAuthentication( registry, {
grantedScopes: [],
} );
},
};

export default {
title: 'Modules/Analytics4/Components/AudienceSegmentation/Dashboard/AudienceCreationNotice',
component: AudienceCreationNotice,
decorators: [
( Story, { args } ) => {
const setupRegistry = ( registry ) => {
provideUserAuthentication( registry, {
grantedScopes: [ EDIT_SCOPE ],
} );

registry.dispatch( CORE_USER ).receiveGetDismissedItems( [] );

registry
Expand Down
Loading

0 comments on commit 7c1e93d

Please sign in to comment.