@automattic/jetpack-connection 0.29.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +12 -0
- package/CHANGELOG.md +622 -0
- package/LICENSE.txt +357 -0
- package/README.md +284 -0
- package/SECURITY.md +38 -0
- package/components/connect-button/index.jsx +70 -0
- package/components/connect-screen/basic/index.jsx +104 -0
- package/components/connect-screen/basic/style.scss +46 -0
- package/components/connect-screen/basic/visual.jsx +109 -0
- package/components/connect-screen/layout/image-slider.jsx +37 -0
- package/components/connect-screen/layout/index.jsx +65 -0
- package/components/connect-screen/layout/style.scss +238 -0
- package/components/connect-screen/required-plan/index.jsx +127 -0
- package/components/connect-screen/required-plan/style.scss +109 -0
- package/components/connect-screen/required-plan/visual.jsx +151 -0
- package/components/connect-user/index.jsx +64 -0
- package/components/connected-plugins/index.jsx +67 -0
- package/components/connection-error-notice/index.jsx +110 -0
- package/components/connection-error-notice/styles.module.scss +97 -0
- package/components/disconnect-card/index.jsx +40 -0
- package/components/disconnect-card/style.scss +85 -0
- package/components/disconnect-dialog/images/disconnect-confirm.jpg +0 -0
- package/components/disconnect-dialog/images/disconnect-thanks.jpg +0 -0
- package/components/disconnect-dialog/index.jsx +409 -0
- package/components/disconnect-dialog/steps/step-disconnect-confirm.jsx +87 -0
- package/components/disconnect-dialog/steps/step-disconnect.jsx +206 -0
- package/components/disconnect-dialog/steps/step-survey.jsx +48 -0
- package/components/disconnect-dialog/steps/step-thank-you.jsx +54 -0
- package/components/disconnect-dialog/style.scss +218 -0
- package/components/disconnect-survey/_jp-connect_disconnect-survey-card.scss +60 -0
- package/components/disconnect-survey/index.jsx +181 -0
- package/components/disconnect-survey/survey-choice.jsx +43 -0
- package/components/in-place-connection/index.jsx +140 -0
- package/components/in-place-connection/style.scss +35 -0
- package/components/manage-connection-dialog/index.jsx +219 -0
- package/components/manage-connection-dialog/style.scss +106 -0
- package/components/use-connection/index.jsx +112 -0
- package/helpers/third-party-cookies-fallback.jsx +10 -0
- package/hooks/use-connection-error-notice/index.jsx +38 -0
- package/hooks/use-product-checkout-workflow/Readme.md +61 -0
- package/hooks/use-product-checkout-workflow/index.jsx +103 -0
- package/hooks/use-restore-connection/index.jsx +64 -0
- package/index.jsx +48 -0
- package/index.native.js +1 -0
- package/package.json +62 -0
- package/state/actions.jsx +166 -0
- package/state/controls.jsx +40 -0
- package/state/reducers.jsx +121 -0
- package/state/resolvers.jsx +32 -0
- package/state/selectors.jsx +35 -0
- package/state/store-holder.jsx +14 -0
- package/state/store-id.jsx +3 -0
- package/state/store.jsx +29 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
.heading {
|
|
2
|
+
margin-bottom: 0;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.notice {
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
margin: 0;
|
|
8
|
+
padding: calc( var( --spacing-base ) * 2 ) calc( var( --spacing-base ) * 3 ) calc( var( --spacing-base ) * 2 ) calc( var( --spacing-base ) * 3 ); // 16px | 24px | 16px | 24px
|
|
9
|
+
font-size: 16px;
|
|
10
|
+
line-height: 22px;
|
|
11
|
+
color: var( --jp-gray-80 );
|
|
12
|
+
border: 1px solid var( --jp-gray );
|
|
13
|
+
border-left: 6px solid var( --jp-red-50 );
|
|
14
|
+
border-radius: var( --jp-border-radius );
|
|
15
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.03), 0 1px 2px rgba(0, 0, 0, 0.06);
|
|
16
|
+
|
|
17
|
+
// notice itself on error status
|
|
18
|
+
&:global(.is-error) {
|
|
19
|
+
background-color: var( --jp-white );
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// notice content
|
|
23
|
+
& :global(.components-notice__content) {
|
|
24
|
+
display: flex;
|
|
25
|
+
margin: 0;
|
|
26
|
+
padding: 12px 4px;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
align-items: flex-start;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// action button
|
|
32
|
+
& :global(.is-link) {
|
|
33
|
+
color: var( --jp-black );
|
|
34
|
+
font-size: 16px;
|
|
35
|
+
font-weight: 600;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// X close button
|
|
39
|
+
& :global(.components-notice__dismiss) {
|
|
40
|
+
align-self: center;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.button, .button:visited, .button:active, .button:hover {
|
|
44
|
+
color: var( --jp-white );
|
|
45
|
+
font-weight: 600;
|
|
46
|
+
font-size: 16px;
|
|
47
|
+
line-height: 24px;
|
|
48
|
+
letter-spacing: -0.01em;
|
|
49
|
+
text-decoration: none;
|
|
50
|
+
cursor: pointer;
|
|
51
|
+
justify-content: center;
|
|
52
|
+
align-items: center;
|
|
53
|
+
padding: 8px 24px;
|
|
54
|
+
margin-left: calc( var( --spacing-base ) * 2 + 24px ); // 40px
|
|
55
|
+
margin-top: 24px;
|
|
56
|
+
background: #000000;
|
|
57
|
+
border-radius: var( --jp-border-radius );
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
&.bigger-than-medium {
|
|
61
|
+
.button, .button:visited, .button:active, .button:hover {
|
|
62
|
+
margin-left: 0;
|
|
63
|
+
margin-top: 0;
|
|
64
|
+
white-space: nowrap;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
& :global(.components-notice__content) {
|
|
68
|
+
flex-direction: row;
|
|
69
|
+
align-items: center;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.error {
|
|
75
|
+
margin-bottom: 25px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.error {
|
|
79
|
+
margin-bottom: 25px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.message {
|
|
83
|
+
margin-right: var( --spacing-base ); // 8px
|
|
84
|
+
flex-grow: 1;
|
|
85
|
+
display: flex;
|
|
86
|
+
|
|
87
|
+
// left icon
|
|
88
|
+
& > svg {
|
|
89
|
+
flex-shrink: 0;
|
|
90
|
+
align-self: flex-start;
|
|
91
|
+
margin-right: calc( var( --spacing-base ) * 2 ); // 16px
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
& :global(.jp-components-spinner) {
|
|
95
|
+
margin-right: calc( var( --spacing-base ) * 2 ); // 16px
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import './style.scss';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Show a card with a title, value and description.
|
|
8
|
+
* Used in the disconnection flow.
|
|
9
|
+
*
|
|
10
|
+
* @param {object} props - The Properties.
|
|
11
|
+
* @returns {React.Component} DisconnectCard - The disconnect card component.
|
|
12
|
+
*/
|
|
13
|
+
const DisconnectCard = props => {
|
|
14
|
+
const { title, value, description } = props;
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className="jp-connection__disconnect-card card">
|
|
18
|
+
<div className="jp-connection__disconnect-card__card-content">
|
|
19
|
+
<p className="jp-connection__disconnect-card__card-headline">{ title }</p>
|
|
20
|
+
{ ( value || description ) && (
|
|
21
|
+
<div className="jp-connection__disconnect-card__card-stat-block">
|
|
22
|
+
<span className="jp-connection__disconnect-card__card-stat">{ value }</span>
|
|
23
|
+
<div className="jp-connection__disconnect-card__card-description">{ description }</div>
|
|
24
|
+
</div>
|
|
25
|
+
) }
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
DisconnectCard.propTypes = {
|
|
32
|
+
/** The title to show on the disconnect card. */
|
|
33
|
+
title: PropTypes.string,
|
|
34
|
+
/** Optional value/ statistic to show. */
|
|
35
|
+
value: PropTypes.oneOfType( [ PropTypes.string, PropTypes.number ] ),
|
|
36
|
+
/** Description to go with the stat value. */
|
|
37
|
+
description: PropTypes.string,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default DisconnectCard;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
@import '@automattic/jetpack-base-styles/style';
|
|
2
|
+
|
|
3
|
+
// Used to show cards in the disconnection flow for active plugins and site benefits.
|
|
4
|
+
.jp-connection__disconnect-card {
|
|
5
|
+
background-color: var( --jp-white );
|
|
6
|
+
width: 800px;
|
|
7
|
+
max-width: 100%;
|
|
8
|
+
padding: 1rem 2rem;
|
|
9
|
+
border: none;
|
|
10
|
+
border-radius: 3px;
|
|
11
|
+
box-shadow: 0 0 15px var( --jp-gray-off );
|
|
12
|
+
margin-left: auto;
|
|
13
|
+
margin-right: auto;
|
|
14
|
+
margin-top: 0;
|
|
15
|
+
margin-bottom: 1rem;
|
|
16
|
+
text-align: left;
|
|
17
|
+
|
|
18
|
+
&__group {
|
|
19
|
+
margin-bottom: 1rem;
|
|
20
|
+
max-width: 100%;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
&__card-content {
|
|
24
|
+
display: block;
|
|
25
|
+
font-size: 0.875rem;
|
|
26
|
+
|
|
27
|
+
@media only screen and (min-width: 782px) {
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: space-between;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&__card-headline,
|
|
35
|
+
.jp-connection__disconnect-card__card-headline { // Override rule from disconnect-modal
|
|
36
|
+
font-size: 1.25rem;
|
|
37
|
+
font-weight: 600;
|
|
38
|
+
margin-top: 0;
|
|
39
|
+
flex-shrink: 0;
|
|
40
|
+
margin-bottom: 0;
|
|
41
|
+
|
|
42
|
+
@media only screen and (min-width: 782px) {
|
|
43
|
+
font-size: 1.5rem;
|
|
44
|
+
margin-right: 1.5rem;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// smaller screens
|
|
48
|
+
@media only screen and (max-width: 782px) {
|
|
49
|
+
// When a headline is above a stats block, add some space.
|
|
50
|
+
& + .jp-disconnect-card__card-stat-block {
|
|
51
|
+
margin-top: 0.5rem;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
&__card-stat-block {
|
|
57
|
+
display: flex;
|
|
58
|
+
align-items: baseline;
|
|
59
|
+
flex-grow: 1;
|
|
60
|
+
|
|
61
|
+
@media only screen and (min-width: 782px) {
|
|
62
|
+
flex-direction: row-reverse;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
&__card-description {
|
|
67
|
+
flex-grow: 1;
|
|
68
|
+
|
|
69
|
+
@media only screen and (min-width: 782px) {
|
|
70
|
+
text-align: right;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
&__card-stat {
|
|
75
|
+
font-size: 1rem;
|
|
76
|
+
font-weight: 600;
|
|
77
|
+
margin-right: 0.5rem;
|
|
78
|
+
|
|
79
|
+
@media only screen and (min-width: 782px) {
|
|
80
|
+
font-size: 1.5rem;
|
|
81
|
+
margin-right: 0;
|
|
82
|
+
margin-left: 1rem;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import jetpackAnalytics from '@automattic/jetpack-analytics';
|
|
2
|
+
import restApi from '@automattic/jetpack-api';
|
|
3
|
+
import { jetpackConfigHas, jetpackConfigGet } from '@automattic/jetpack-config';
|
|
4
|
+
import { Modal } from '@wordpress/components';
|
|
5
|
+
import { __ } from '@wordpress/i18n';
|
|
6
|
+
import PropTypes from 'prop-types';
|
|
7
|
+
import React, { useMemo, useEffect, useCallback, useState } from 'react';
|
|
8
|
+
import './style.scss';
|
|
9
|
+
import StepDisconnect from './steps/step-disconnect';
|
|
10
|
+
import StepDisconnectConfirm from './steps/step-disconnect-confirm';
|
|
11
|
+
import StepSurvey from './steps/step-survey';
|
|
12
|
+
import StepThankYou from './steps/step-thank-you';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The RNA Disconnect Dialog component.
|
|
16
|
+
*
|
|
17
|
+
* @param {object} props -- The properties.
|
|
18
|
+
* @returns {React.Component} The `DisconnectDialog` component.
|
|
19
|
+
*/
|
|
20
|
+
const DisconnectDialog = props => {
|
|
21
|
+
const [ isDisconnecting, setIsDisconnecting ] = useState( false );
|
|
22
|
+
const [ isDisconnected, setIsDisconnected ] = useState( false );
|
|
23
|
+
const [ disconnectError, setDisconnectError ] = useState( false );
|
|
24
|
+
const [ isProvidingFeedback, setIsProvidingFeedback ] = useState( false );
|
|
25
|
+
const [ isFeedbackProvided, setIsFeedbackProvided ] = useState( false );
|
|
26
|
+
const [ isSubmittingFeedback, setIsSubmittingFeedback ] = useState( false );
|
|
27
|
+
|
|
28
|
+
const {
|
|
29
|
+
apiRoot,
|
|
30
|
+
apiNonce,
|
|
31
|
+
connectedPlugins,
|
|
32
|
+
title,
|
|
33
|
+
pluginScreenDisconnectCallback,
|
|
34
|
+
onDisconnected,
|
|
35
|
+
onError,
|
|
36
|
+
disconnectStepComponent,
|
|
37
|
+
context,
|
|
38
|
+
connectedUser,
|
|
39
|
+
connectedSiteId,
|
|
40
|
+
isOpen,
|
|
41
|
+
onClose,
|
|
42
|
+
} = props;
|
|
43
|
+
|
|
44
|
+
let disconnectingPlugin = '';
|
|
45
|
+
if ( jetpackConfigHas( 'consumer_slug' ) ) {
|
|
46
|
+
disconnectingPlugin = jetpackConfigGet( 'consumer_slug' );
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const defaultTracksArgs = useMemo( () => {
|
|
50
|
+
return {
|
|
51
|
+
context: context,
|
|
52
|
+
plugin: disconnectingPlugin,
|
|
53
|
+
};
|
|
54
|
+
}, [ context, disconnectingPlugin ] );
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Initialize the REST API.
|
|
58
|
+
*/
|
|
59
|
+
useEffect( () => {
|
|
60
|
+
restApi.setApiRoot( apiRoot );
|
|
61
|
+
restApi.setApiNonce( apiNonce );
|
|
62
|
+
}, [ apiRoot, apiNonce ] );
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Initialize tracks with user data.
|
|
66
|
+
* Should run when we have a connected user.
|
|
67
|
+
*/
|
|
68
|
+
useEffect( () => {
|
|
69
|
+
if ( connectedUser && connectedUser.ID && connectedUser.login ) {
|
|
70
|
+
jetpackAnalytics.initialize( connectedUser.ID, connectedUser.login );
|
|
71
|
+
}
|
|
72
|
+
}, [ connectedUser, connectedUser.ID, connectedUser.login ] );
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Run when the disconnect dialog is opened
|
|
76
|
+
*/
|
|
77
|
+
useEffect( () => {
|
|
78
|
+
if ( isOpen ) {
|
|
79
|
+
jetpackAnalytics.tracks.recordEvent( 'jetpack_disconnect_dialog_open', defaultTracksArgs );
|
|
80
|
+
}
|
|
81
|
+
}, [ isOpen, defaultTracksArgs ] );
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Keep track of the steps that are presented
|
|
85
|
+
*/
|
|
86
|
+
useEffect( () => {
|
|
87
|
+
// Don't do anything if the dialog is not open.
|
|
88
|
+
if ( ! isOpen ) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if ( ! isDisconnected ) {
|
|
93
|
+
jetpackAnalytics.tracks.recordEvent(
|
|
94
|
+
'jetpack_disconnect_dialog_step',
|
|
95
|
+
Object.assign( {}, { step: 'disconnect' }, defaultTracksArgs )
|
|
96
|
+
);
|
|
97
|
+
} else if ( isDisconnected && ! isProvidingFeedback && ! isFeedbackProvided ) {
|
|
98
|
+
jetpackAnalytics.tracks.recordEvent(
|
|
99
|
+
'jetpack_disconnect_dialog_step',
|
|
100
|
+
Object.assign( {}, { step: 'disconnect_confirm' }, defaultTracksArgs )
|
|
101
|
+
);
|
|
102
|
+
} else if ( isProvidingFeedback && ! isFeedbackProvided ) {
|
|
103
|
+
jetpackAnalytics.tracks.recordEvent(
|
|
104
|
+
'jetpack_disconnect_dialog_step',
|
|
105
|
+
Object.assign( {}, { step: 'survey' }, defaultTracksArgs )
|
|
106
|
+
);
|
|
107
|
+
} else if ( isFeedbackProvided ) {
|
|
108
|
+
jetpackAnalytics.tracks.recordEvent(
|
|
109
|
+
'jetpack_disconnect_dialog_step',
|
|
110
|
+
Object.assign( {}, { step: 'thank_you' }, defaultTracksArgs )
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}, [ isOpen, isDisconnected, isProvidingFeedback, isFeedbackProvided, defaultTracksArgs ] );
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Disconnect the site.
|
|
117
|
+
* Uses the rest API to remove the Jetpack connection.
|
|
118
|
+
*/
|
|
119
|
+
const _disconnect = useCallback( () => {
|
|
120
|
+
restApi
|
|
121
|
+
.disconnectSite()
|
|
122
|
+
.then( () => {
|
|
123
|
+
setIsDisconnecting( false );
|
|
124
|
+
setIsDisconnected( true );
|
|
125
|
+
} )
|
|
126
|
+
.catch( error => {
|
|
127
|
+
setIsDisconnecting( false );
|
|
128
|
+
setDisconnectError( error );
|
|
129
|
+
|
|
130
|
+
if ( onError ) {
|
|
131
|
+
onError( error );
|
|
132
|
+
}
|
|
133
|
+
} );
|
|
134
|
+
}, [ setIsDisconnecting, setIsDisconnected, setDisconnectError, onError ] );
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Submit the optional survey following disconnection.
|
|
138
|
+
*/
|
|
139
|
+
const _submitSurvey = useCallback(
|
|
140
|
+
( surveyData, tracksSurveyData ) => {
|
|
141
|
+
// Send survey response to wpcom
|
|
142
|
+
const base = 'https://public-api.wordpress.com';
|
|
143
|
+
const path = '/wpcom/v2/marketing/feedback-survey';
|
|
144
|
+
const method = 'POST';
|
|
145
|
+
|
|
146
|
+
setIsSubmittingFeedback( true );
|
|
147
|
+
|
|
148
|
+
// We cannot use `@wordpress/api-fetch` here since it unconditionally sends
|
|
149
|
+
// the `X-WP-Nonce` header, which is disallowed by WordPress.com.
|
|
150
|
+
// If the submission receives an error, there's not really anything the user is able to do to fix it.
|
|
151
|
+
// In these cases, just go ahead and show the last survey step.
|
|
152
|
+
fetch( base + path, {
|
|
153
|
+
method: method,
|
|
154
|
+
headers: {
|
|
155
|
+
'Content-Type': 'application/json',
|
|
156
|
+
Accept: 'application/json',
|
|
157
|
+
},
|
|
158
|
+
body: JSON.stringify( surveyData ),
|
|
159
|
+
} )
|
|
160
|
+
.then( result => result.json() )
|
|
161
|
+
.then( jsonResponse => {
|
|
162
|
+
// response received
|
|
163
|
+
if ( true === jsonResponse.success ) {
|
|
164
|
+
// Send a tracks event for survey submission.
|
|
165
|
+
jetpackAnalytics.tracks.recordEvent(
|
|
166
|
+
'jetpack_disconnect_survey_submit',
|
|
167
|
+
tracksSurveyData
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
setIsFeedbackProvided( true );
|
|
171
|
+
setIsSubmittingFeedback( false );
|
|
172
|
+
} else {
|
|
173
|
+
throw new Error( 'Survey endpoint returned error code ' + jsonResponse.code );
|
|
174
|
+
}
|
|
175
|
+
} )
|
|
176
|
+
.catch( error => {
|
|
177
|
+
jetpackAnalytics.tracks.recordEvent(
|
|
178
|
+
'jetpack_disconnect_survey_error',
|
|
179
|
+
Object.assign( {}, { error: error.message }, tracksSurveyData )
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
setIsFeedbackProvided( true );
|
|
183
|
+
setIsSubmittingFeedback( false );
|
|
184
|
+
} );
|
|
185
|
+
},
|
|
186
|
+
[ setIsSubmittingFeedback, setIsFeedbackProvided ]
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Disconnect - Triggered upon clicking the 'Disconnect' button.
|
|
191
|
+
*/
|
|
192
|
+
const handleDisconnect = useCallback(
|
|
193
|
+
e => {
|
|
194
|
+
e && e.preventDefault();
|
|
195
|
+
|
|
196
|
+
setDisconnectError( false );
|
|
197
|
+
setIsDisconnecting( true );
|
|
198
|
+
|
|
199
|
+
// Detect the plugin context, where the plugin needs to be deactivated.
|
|
200
|
+
if ( context === 'plugins' ) {
|
|
201
|
+
// Use a callback function to handle deactivating the plugin.
|
|
202
|
+
// This should effectively short-circuit the disconnect flow by redirecting to deactivate the plugin.
|
|
203
|
+
if ( pluginScreenDisconnectCallback ) {
|
|
204
|
+
pluginScreenDisconnectCallback( e );
|
|
205
|
+
}
|
|
206
|
+
// Do not disconnect if context is the plugin screen, the plugin deactivation routine will handle disconnection.
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Default to making the disconnect API call here.
|
|
211
|
+
_disconnect();
|
|
212
|
+
},
|
|
213
|
+
[ setDisconnectError, setIsDisconnecting, pluginScreenDisconnectCallback, context, _disconnect ]
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const trackModalClick = useCallback(
|
|
217
|
+
target => jetpackAnalytics.tracks.recordEvent( target, defaultTracksArgs ),
|
|
218
|
+
[ defaultTracksArgs ]
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Do we have the necessary data to be able to submit a survey?
|
|
223
|
+
* Need to have the ID of the connected user and the ID of the connected site.
|
|
224
|
+
*/
|
|
225
|
+
const canProvideFeedback = useCallback( () => {
|
|
226
|
+
return !! ( connectedUser.ID && connectedSiteId );
|
|
227
|
+
}, [ connectedUser, connectedSiteId ] );
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Submit Survey - triggered by clicking on the "Submit Feedback" button.
|
|
231
|
+
* Assembles the survey response.
|
|
232
|
+
*/
|
|
233
|
+
const handleSubmitSurvey = useCallback(
|
|
234
|
+
( surveyAnswerId, surveyAnswerText, e ) => {
|
|
235
|
+
e && e.preventDefault();
|
|
236
|
+
|
|
237
|
+
// We do not have the information needed to record the response.
|
|
238
|
+
// return early and move to the last step in the flow anyway.
|
|
239
|
+
if ( ! canProvideFeedback() ) {
|
|
240
|
+
setIsFeedbackProvided( true );
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Format the survey data for submission.
|
|
245
|
+
const surveyData = {
|
|
246
|
+
site_id: connectedSiteId,
|
|
247
|
+
user_id: connectedUser.ID,
|
|
248
|
+
survey_id: 'jetpack-plugin-disconnect',
|
|
249
|
+
survey_responses: {
|
|
250
|
+
'why-cancel': {
|
|
251
|
+
response: surveyAnswerId,
|
|
252
|
+
text: surveyAnswerText ? surveyAnswerText : null,
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
// Additional data for analytics to see where disconnections happened from.
|
|
258
|
+
const tracksSurveyData = Object.assign( {}, defaultTracksArgs, {
|
|
259
|
+
disconnect_reason: surveyAnswerId,
|
|
260
|
+
} );
|
|
261
|
+
|
|
262
|
+
_submitSurvey( surveyData, tracksSurveyData );
|
|
263
|
+
},
|
|
264
|
+
[
|
|
265
|
+
_submitSurvey,
|
|
266
|
+
setIsFeedbackProvided,
|
|
267
|
+
canProvideFeedback,
|
|
268
|
+
connectedSiteId,
|
|
269
|
+
connectedUser,
|
|
270
|
+
defaultTracksArgs,
|
|
271
|
+
]
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Close modal and fire 'onDisconnected' callback if exists.
|
|
276
|
+
* Triggered upon clicking the 'Back To WordPress' button.
|
|
277
|
+
*/
|
|
278
|
+
const backToWordpress = useCallback(
|
|
279
|
+
e => {
|
|
280
|
+
e && e.preventDefault();
|
|
281
|
+
|
|
282
|
+
if ( onDisconnected ) {
|
|
283
|
+
onDisconnected();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
onClose();
|
|
287
|
+
},
|
|
288
|
+
[ onDisconnected, onClose ]
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Update the local state to show the survey step.
|
|
293
|
+
*/
|
|
294
|
+
const handleProvideFeedback = useCallback(
|
|
295
|
+
e => {
|
|
296
|
+
e && e.preventDefault();
|
|
297
|
+
setIsProvidingFeedback( true );
|
|
298
|
+
},
|
|
299
|
+
[ setIsProvidingFeedback ]
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Determine what step to show based on the current state
|
|
304
|
+
*
|
|
305
|
+
* @returns { React.Component|undefined } - component for current step
|
|
306
|
+
*/
|
|
307
|
+
const getCurrentStep = () => {
|
|
308
|
+
if ( ! isDisconnected ) {
|
|
309
|
+
// Disconnection screen.
|
|
310
|
+
return (
|
|
311
|
+
<StepDisconnect
|
|
312
|
+
title={ title }
|
|
313
|
+
connectedPlugins={ connectedPlugins }
|
|
314
|
+
// Component that renders as part of the disconnect step, if passed.
|
|
315
|
+
disconnectStepComponent={ disconnectStepComponent }
|
|
316
|
+
isDisconnecting={ isDisconnecting }
|
|
317
|
+
closeModal={ onClose }
|
|
318
|
+
onDisconnect={ handleDisconnect }
|
|
319
|
+
disconnectError={ disconnectError }
|
|
320
|
+
context={ context } // Where is the modal showing? ( most important for when it loads on the plugins page )
|
|
321
|
+
disconnectingPlugin={ disconnectingPlugin } // Which plugin is initiating the disconnect.
|
|
322
|
+
trackModalClick={ trackModalClick }
|
|
323
|
+
/>
|
|
324
|
+
);
|
|
325
|
+
} else if ( isDisconnected && ! isProvidingFeedback && ! isFeedbackProvided ) {
|
|
326
|
+
// Confirm the disconnection, ask user about providing feedback.
|
|
327
|
+
return (
|
|
328
|
+
<StepDisconnectConfirm
|
|
329
|
+
canProvideFeedback={ canProvideFeedback() }
|
|
330
|
+
onProvideFeedback={ handleProvideFeedback }
|
|
331
|
+
onExit={ backToWordpress }
|
|
332
|
+
/>
|
|
333
|
+
);
|
|
334
|
+
} else if ( isProvidingFeedback && ! isFeedbackProvided ) {
|
|
335
|
+
return (
|
|
336
|
+
<StepSurvey
|
|
337
|
+
isSubmittingFeedback={ isSubmittingFeedback }
|
|
338
|
+
onFeedBackProvided={ handleSubmitSurvey }
|
|
339
|
+
onExit={ backToWordpress }
|
|
340
|
+
/>
|
|
341
|
+
);
|
|
342
|
+
} else if ( isFeedbackProvided ) {
|
|
343
|
+
return <StepThankYou onExit={ backToWordpress } />;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return undefined;
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
return (
|
|
350
|
+
<>
|
|
351
|
+
{ isOpen && (
|
|
352
|
+
<Modal
|
|
353
|
+
title=""
|
|
354
|
+
contentLabel={ title }
|
|
355
|
+
aria={ {
|
|
356
|
+
labelledby: 'jp-connection__disconnect-dialog__heading',
|
|
357
|
+
} }
|
|
358
|
+
onRequestClose={ onClose }
|
|
359
|
+
shouldCloseOnClickOutside={ false }
|
|
360
|
+
shouldCloseOnEsc={ false }
|
|
361
|
+
isDismissible={ false }
|
|
362
|
+
className={
|
|
363
|
+
'jp-connection__disconnect-dialog' +
|
|
364
|
+
( isDisconnected ? ' jp-connection__disconnect-dialog__success' : '' )
|
|
365
|
+
}
|
|
366
|
+
>
|
|
367
|
+
{ getCurrentStep() }
|
|
368
|
+
</Modal>
|
|
369
|
+
) }
|
|
370
|
+
</>
|
|
371
|
+
);
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
DisconnectDialog.propTypes = {
|
|
375
|
+
/** API root URL, required. */
|
|
376
|
+
apiRoot: PropTypes.string.isRequired,
|
|
377
|
+
/** API Nonce, required. */
|
|
378
|
+
apiNonce: PropTypes.string.isRequired,
|
|
379
|
+
/** The modal title. */
|
|
380
|
+
title: PropTypes.string,
|
|
381
|
+
/** The callback to be called upon disconnection success. */
|
|
382
|
+
onDisconnected: PropTypes.func,
|
|
383
|
+
/** The callback to be called upon disconnection failure. */
|
|
384
|
+
onError: PropTypes.func,
|
|
385
|
+
/** The context in which this component is being used. */
|
|
386
|
+
context: PropTypes.string,
|
|
387
|
+
/** Plugins that are using the Jetpack connection. */
|
|
388
|
+
connectedPlugins: PropTypes.oneOfType( [ PropTypes.array, PropTypes.object ] ),
|
|
389
|
+
/** Callback function that is called just before the request to disconnect is made when the context is "plugins". */
|
|
390
|
+
pluginScreenDisconnectCallback: PropTypes.func,
|
|
391
|
+
/** A component to render as part of the disconnect step. */
|
|
392
|
+
disconnectStepComponent: PropTypes.element,
|
|
393
|
+
/** An object representing the connected user. */
|
|
394
|
+
connectedUser: PropTypes.object,
|
|
395
|
+
/** ID of the currently connected site. */
|
|
396
|
+
connectedSiteId: PropTypes.number,
|
|
397
|
+
/** Whether or not the dialog modal should be open. */
|
|
398
|
+
isOpen: PropTypes.bool,
|
|
399
|
+
/** Callback function for when the modal closes. */
|
|
400
|
+
onClose: PropTypes.func,
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
DisconnectDialog.defaultProps = {
|
|
404
|
+
title: __( 'Are you sure you want to disconnect?', 'jetpack' ),
|
|
405
|
+
context: 'jetpack-dashboard',
|
|
406
|
+
connectedUser: {}, // Pass empty object to avoid undefined errors.
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
export default DisconnectDialog;
|