Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement/#8165 - Implement the Audience Creation Notice OAuth flow #9206

Merged
merged 16 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading