@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,181 @@
|
|
|
1
|
+
import { Button } from '@wordpress/components';
|
|
2
|
+
import { __ } from '@wordpress/i18n';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import React, { useCallback, useState } from 'react';
|
|
5
|
+
import SurveyChoice from './survey-choice';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Handles showing the disconnect survey.
|
|
9
|
+
*
|
|
10
|
+
* @param {object} props - The component props.
|
|
11
|
+
* @returns {React.Component} - DisconnectSurvey component.
|
|
12
|
+
*/
|
|
13
|
+
const DisconnectSurvey = props => {
|
|
14
|
+
const { onSubmit, isSubmittingFeedback } = props;
|
|
15
|
+
const [ selectedAnswer, setSelectedAnswer ] = useState();
|
|
16
|
+
const [ customResponse, setCustomResponse ] = useState();
|
|
17
|
+
|
|
18
|
+
const options = [
|
|
19
|
+
{
|
|
20
|
+
id: 'troubleshooting',
|
|
21
|
+
answerText: __( "Troubleshooting - I'll be reconnecting afterwards.", 'jetpack' ),
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'not-working',
|
|
25
|
+
answerText: __( "I can't get it to work.", 'jetpack' ),
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'slowed-down-site',
|
|
29
|
+
answerText: __( 'It slowed down my site.', 'jetpack' ),
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: 'buggy',
|
|
33
|
+
answerText: __( "It's buggy.", 'jetpack' ),
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'what-does-it-do',
|
|
37
|
+
answerText: __( "I don't know what it does.", 'jetpack' ),
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const customOption = {
|
|
42
|
+
id: 'another-reason',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Handle Submission of the survey.
|
|
47
|
+
* Will send the survey response to the collection endpoint.
|
|
48
|
+
*/
|
|
49
|
+
const handleSurveySubmit = useCallback( () => {
|
|
50
|
+
const answerText = selectedAnswer === customOption.id ? customResponse : '';
|
|
51
|
+
onSubmit( selectedAnswer, answerText );
|
|
52
|
+
}, [ onSubmit, customOption.id, customResponse, selectedAnswer ] );
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Handle input into the custom response field.
|
|
56
|
+
*
|
|
57
|
+
* @param {object} e - onChange event for the custom input
|
|
58
|
+
*/
|
|
59
|
+
const handleCustomResponse = useCallback(
|
|
60
|
+
e => {
|
|
61
|
+
const value = e.target.value;
|
|
62
|
+
e.stopPropagation();
|
|
63
|
+
setCustomResponse( value );
|
|
64
|
+
},
|
|
65
|
+
[ setCustomResponse ]
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Checks to see if an option is the currently selected option, returns a css class name if it matches.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} optionId - ID of the option to check for.
|
|
72
|
+
* @returns {string} - The "selected" class if this option is currently selected.
|
|
73
|
+
*/
|
|
74
|
+
const selectedClass = optionId => {
|
|
75
|
+
if ( optionId === selectedAnswer ) {
|
|
76
|
+
return 'jp-connect__disconnect-survey-card--selected';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return '';
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Event handler for keyboard events on the answer blocks.
|
|
84
|
+
*
|
|
85
|
+
* @param {string} answerId - The slug of the answer that has been selected.
|
|
86
|
+
* @param {object} e - Keydown event.
|
|
87
|
+
*/
|
|
88
|
+
const handleAnswerKeyDown = useCallback(
|
|
89
|
+
( answerId, e ) => {
|
|
90
|
+
switch ( e.key ) {
|
|
91
|
+
case 'Enter':
|
|
92
|
+
case 'Space':
|
|
93
|
+
case 'Spacebar':
|
|
94
|
+
case ' ':
|
|
95
|
+
setSelectedAnswer( answerId );
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
[ setSelectedAnswer ]
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Show all the survey options from the options array.
|
|
104
|
+
*
|
|
105
|
+
* @returns {React.ElementType []} - Mapped array of rendered survey options.
|
|
106
|
+
*/
|
|
107
|
+
const renderOptions = () => {
|
|
108
|
+
return options.map( option => {
|
|
109
|
+
return (
|
|
110
|
+
<SurveyChoice
|
|
111
|
+
id={ option.id }
|
|
112
|
+
onClick={ setSelectedAnswer }
|
|
113
|
+
onKeyDown={ handleAnswerKeyDown }
|
|
114
|
+
className={ 'card jp-connect__disconnect-survey-card ' + selectedClass( option.id ) }
|
|
115
|
+
>
|
|
116
|
+
<p className="jp-connect__disconnect-survey-card__answer">{ option.answerText }</p>
|
|
117
|
+
</SurveyChoice>
|
|
118
|
+
);
|
|
119
|
+
} );
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Show the custom input survey option.
|
|
124
|
+
* Contains an input field for a custom response.
|
|
125
|
+
*
|
|
126
|
+
* @returns {React.ElementType} - The custom survey option with an input field.
|
|
127
|
+
*/
|
|
128
|
+
const renderCustomOption = () => {
|
|
129
|
+
return (
|
|
130
|
+
<SurveyChoice
|
|
131
|
+
id={ customOption.id }
|
|
132
|
+
key={ customOption.id }
|
|
133
|
+
onClick={ setSelectedAnswer }
|
|
134
|
+
onKeyDown={ handleAnswerKeyDown }
|
|
135
|
+
className={ 'card jp-connect__disconnect-survey-card ' + selectedClass( customOption.id ) }
|
|
136
|
+
>
|
|
137
|
+
<p className="jp-connect__disconnect-survey-card__answer">
|
|
138
|
+
{ __( 'Other:', 'jetpack' ) }{ ' ' }
|
|
139
|
+
<input
|
|
140
|
+
placeholder={ __( 'share your experience', 'jetpack' ) }
|
|
141
|
+
className="jp-connect__disconnect-survey-card__input"
|
|
142
|
+
type="text"
|
|
143
|
+
value={ customResponse }
|
|
144
|
+
onChange={ handleCustomResponse }
|
|
145
|
+
maxLength={ 1000 } // Limit response length.
|
|
146
|
+
/>
|
|
147
|
+
</p>
|
|
148
|
+
</SurveyChoice>
|
|
149
|
+
);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<React.Fragment>
|
|
154
|
+
<div className="jp-connection__disconnect-dialog__survey">
|
|
155
|
+
{ renderOptions() }
|
|
156
|
+
{ renderCustomOption() }
|
|
157
|
+
</div>
|
|
158
|
+
<p>
|
|
159
|
+
<Button
|
|
160
|
+
disabled={ ! selectedAnswer || isSubmittingFeedback }
|
|
161
|
+
variant="primary"
|
|
162
|
+
onClick={ handleSurveySubmit }
|
|
163
|
+
className="jp-connection__disconnect-dialog__btn-back-to-wp"
|
|
164
|
+
>
|
|
165
|
+
{ isSubmittingFeedback
|
|
166
|
+
? __( 'Submitting…', 'jetpack' )
|
|
167
|
+
: __( 'Submit Feedback', 'jetpack', /* dummy arg to avoid bad minification */ 0 ) }
|
|
168
|
+
</Button>
|
|
169
|
+
</p>
|
|
170
|
+
</React.Fragment>
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
DisconnectSurvey.PropTypes = {
|
|
175
|
+
/** Callback handler function for when the survey response is submitted. */
|
|
176
|
+
onSubmit: PropTypes.func,
|
|
177
|
+
/** If the survey feedback is currently being saved/ submitted */
|
|
178
|
+
isSubmittingFeedback: PropTypes.bool,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export default DisconnectSurvey;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React, { useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
import './_jp-connect_disconnect-survey-card.scss';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SurveyChoice - Present one choice in the survey.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} props.id - The ID/slug string of the survey option
|
|
9
|
+
* @param {Function} props.onClick - Event handler for clicking on the survey option.
|
|
10
|
+
* @param {Function} props.onKeydown - Event handler for pressing a key on the survey option.
|
|
11
|
+
* @param {React.ElementType} props.children - Any passed elements as children to this component.
|
|
12
|
+
* @param {string} props.className - A class name to apply to the survey choice.
|
|
13
|
+
* @returns {React.Component} SurveyChoice - The SurveyChoice component.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const SurveyChoice = props => {
|
|
17
|
+
const { id, onClick, onKeyDown, children, className } = props;
|
|
18
|
+
|
|
19
|
+
const handleClick = useCallback( () => {
|
|
20
|
+
onClick( id );
|
|
21
|
+
}, [ id, onClick ] );
|
|
22
|
+
|
|
23
|
+
const handleKeyDown = useCallback(
|
|
24
|
+
e => {
|
|
25
|
+
onKeyDown( id, e );
|
|
26
|
+
},
|
|
27
|
+
[ id, onKeyDown ]
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div
|
|
32
|
+
tabIndex="0"
|
|
33
|
+
role="button"
|
|
34
|
+
onClick={ handleClick }
|
|
35
|
+
onKeyDown={ handleKeyDown }
|
|
36
|
+
className={ 'card jp-connect__disconnect-survey-card ' + className }
|
|
37
|
+
>
|
|
38
|
+
{ children }
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default SurveyChoice;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { __ } from '@wordpress/i18n';
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import React, { useEffect, useRef } from 'react';
|
|
4
|
+
|
|
5
|
+
import './style.scss';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The in-place connection component.
|
|
9
|
+
*
|
|
10
|
+
* @param {object} props -- The properties.
|
|
11
|
+
* @param {string} props.title -- Element title.
|
|
12
|
+
* @param {boolean} props.isLoading -- Whether the element is still loading.
|
|
13
|
+
* @param {string|number} props.width -- Iframe width.
|
|
14
|
+
* @param {string|number} props.height -- Iframe height.
|
|
15
|
+
* @param {boolean} props.displayTOS -- Whether the site has connection owner connected.
|
|
16
|
+
* @param {boolean} props.scrollToIframe -- Whether we need to auto-scroll the window upon element rendering.
|
|
17
|
+
* @param {string} props.connectUrl -- The connection URL.
|
|
18
|
+
* @param {Function} props.onComplete -- The callback to be called upon complete of the connection process.
|
|
19
|
+
* @param {Function} props.onThirdPartyCookiesBlocked -- The callback to be called if third-party cookies are disabled.
|
|
20
|
+
* @param {string} props.location -- Component location identifier passed to WP.com.
|
|
21
|
+
* @returns {React.Component} The in-place connection component.
|
|
22
|
+
*/
|
|
23
|
+
const InPlaceConnection = props => {
|
|
24
|
+
const {
|
|
25
|
+
title,
|
|
26
|
+
isLoading,
|
|
27
|
+
width,
|
|
28
|
+
displayTOS,
|
|
29
|
+
scrollToIframe,
|
|
30
|
+
connectUrl,
|
|
31
|
+
onComplete,
|
|
32
|
+
onThirdPartyCookiesBlocked,
|
|
33
|
+
location,
|
|
34
|
+
} = props;
|
|
35
|
+
let { height } = props;
|
|
36
|
+
|
|
37
|
+
const iframeWrapRef = useRef();
|
|
38
|
+
const iframeRef = useRef();
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Handles messages received from inside the iframe.
|
|
42
|
+
*
|
|
43
|
+
* @param {object} e -- Event object.
|
|
44
|
+
*/
|
|
45
|
+
const receiveData = e => {
|
|
46
|
+
if ( ! iframeRef.current || e.source !== iframeRef.current.contentWindow ) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
switch ( e.data ) {
|
|
51
|
+
case 'close':
|
|
52
|
+
// Remove listener, our job here is done.
|
|
53
|
+
window.removeEventListener( 'message', receiveData );
|
|
54
|
+
|
|
55
|
+
if ( onComplete ) {
|
|
56
|
+
onComplete();
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
case 'wpcom_nocookie':
|
|
60
|
+
// Third-party cookies blocked.
|
|
61
|
+
if ( onThirdPartyCookiesBlocked ) {
|
|
62
|
+
onThirdPartyCookiesBlocked();
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
useEffect(
|
|
69
|
+
/**
|
|
70
|
+
* The component initialization.
|
|
71
|
+
*/
|
|
72
|
+
() => {
|
|
73
|
+
// Scroll to the iframe container
|
|
74
|
+
if ( scrollToIframe ) {
|
|
75
|
+
window.scrollTo( 0, iframeWrapRef.current.offsetTop - 10 );
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Add an event listener to identify successful authorization via iframe.
|
|
79
|
+
window.addEventListener( 'message', receiveData );
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// The URL looks like https://jetpack.wordpress.com/jetpack.authorize_iframe/1/. We need to include the trailing
|
|
84
|
+
// slash below so that we don't end up with something like /jetpack.authorize_iframe_iframe/
|
|
85
|
+
let src = connectUrl.replace( 'authorize/', 'authorize_iframe/' );
|
|
86
|
+
|
|
87
|
+
if ( ! src.includes( '?' ) ) {
|
|
88
|
+
src += '?';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if ( displayTOS ) {
|
|
92
|
+
src += '&display-tos';
|
|
93
|
+
height = ( parseInt( height ) + 50 ).toString();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
src += '&iframe_height=' + parseInt( height );
|
|
97
|
+
|
|
98
|
+
if ( location ) {
|
|
99
|
+
src += '&iframe_source=' + location;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<div className="dops-card fade-in jp-iframe-wrap" ref={ iframeWrapRef }>
|
|
104
|
+
<h1>{ title }</h1>
|
|
105
|
+
{ isLoading ? (
|
|
106
|
+
<p>{ __( 'Loading…', 'jetpack' ) }</p>
|
|
107
|
+
) : (
|
|
108
|
+
<iframe
|
|
109
|
+
title={ title }
|
|
110
|
+
width={ width }
|
|
111
|
+
height={ height }
|
|
112
|
+
src={ src }
|
|
113
|
+
ref={ iframeRef }
|
|
114
|
+
></iframe>
|
|
115
|
+
) }
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
InPlaceConnection.propTypes = {
|
|
121
|
+
title: PropTypes.string.isRequired,
|
|
122
|
+
isLoading: PropTypes.bool,
|
|
123
|
+
width: PropTypes.string,
|
|
124
|
+
height: PropTypes.string,
|
|
125
|
+
connectUrl: PropTypes.string.isRequired,
|
|
126
|
+
displayTOS: PropTypes.bool.isRequired,
|
|
127
|
+
scrollToIframe: PropTypes.bool,
|
|
128
|
+
onComplete: PropTypes.func,
|
|
129
|
+
onThirdPartyCookiesBlocked: PropTypes.func,
|
|
130
|
+
location: PropTypes.string,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
InPlaceConnection.defaultProps = {
|
|
134
|
+
isLoading: false,
|
|
135
|
+
height: '300',
|
|
136
|
+
width: '100%',
|
|
137
|
+
scrollToIframe: false,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export default InPlaceConnection;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
.jp-iframe-wrap {
|
|
2
|
+
text-align: center;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.fade-in {
|
|
6
|
+
animation: fadeIn ease 1.5s;
|
|
7
|
+
-webkit-animation: fadeIn ease 1.5s;
|
|
8
|
+
-moz-animation: fadeIn ease 1.5s;
|
|
9
|
+
-o-animation: fadeIn ease 1.5s;
|
|
10
|
+
-ms-animation: fadeIn ease 1.5s;
|
|
11
|
+
}
|
|
12
|
+
@keyframes fadeIn {
|
|
13
|
+
0% {opacity:0;}
|
|
14
|
+
100% {opacity:1;}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@-moz-keyframes fadeIn {
|
|
18
|
+
0% {opacity:0;}
|
|
19
|
+
100% {opacity:1;}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@-webkit-keyframes fadeIn {
|
|
23
|
+
0% {opacity:0;}
|
|
24
|
+
100% {opacity:1;}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@-o-keyframes fadeIn {
|
|
28
|
+
0% {opacity:0;}
|
|
29
|
+
100% {opacity:1;}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@-ms-keyframes fadeIn {
|
|
33
|
+
0% {opacity:0;}
|
|
34
|
+
100% {opacity:1;}
|
|
35
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { Button, getRedirectUrl, Text } from '@automattic/jetpack-components';
|
|
5
|
+
import { ExternalLink, Modal } from '@wordpress/components';
|
|
6
|
+
import { createInterpolateElement } from '@wordpress/element';
|
|
7
|
+
import { __ } from '@wordpress/i18n';
|
|
8
|
+
import { Icon, chevronRight, external } from '@wordpress/icons';
|
|
9
|
+
import classNames from 'classnames';
|
|
10
|
+
import PropTypes from 'prop-types';
|
|
11
|
+
import React, { useCallback, useState } from 'react';
|
|
12
|
+
/**
|
|
13
|
+
* Internal dependencies
|
|
14
|
+
*/
|
|
15
|
+
import DisconnectDialog from '../disconnect-dialog';
|
|
16
|
+
import './style.scss';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The RNA Manage Connection Dialog component.
|
|
20
|
+
*
|
|
21
|
+
* @param {object} props -- The properties.
|
|
22
|
+
* @returns {React.Component} The `ManageConnectionDialog` component.
|
|
23
|
+
*/
|
|
24
|
+
const ManageConnectionDialog = props => {
|
|
25
|
+
const {
|
|
26
|
+
title,
|
|
27
|
+
apiRoot,
|
|
28
|
+
apiNonce,
|
|
29
|
+
connectedPlugins,
|
|
30
|
+
onDisconnected,
|
|
31
|
+
context,
|
|
32
|
+
connectedUser,
|
|
33
|
+
connectedSiteId,
|
|
34
|
+
isOpen,
|
|
35
|
+
onClose,
|
|
36
|
+
} = props;
|
|
37
|
+
|
|
38
|
+
const [ isDisconnectDialogOpen, setIsDisconnectDialogOpen ] = useState( false );
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Open the Disconnect Dialog.
|
|
42
|
+
*/
|
|
43
|
+
const openDisconnectDialog = useCallback(
|
|
44
|
+
e => {
|
|
45
|
+
e && e.preventDefault();
|
|
46
|
+
setIsDisconnectDialogOpen( true );
|
|
47
|
+
},
|
|
48
|
+
[ setIsDisconnectDialogOpen ]
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Close the Disconnect Dialog.
|
|
53
|
+
*/
|
|
54
|
+
const closeDisconnectDialog = useCallback(
|
|
55
|
+
e => {
|
|
56
|
+
e && e.preventDefault();
|
|
57
|
+
setIsDisconnectDialogOpen( false );
|
|
58
|
+
},
|
|
59
|
+
[ setIsDisconnectDialogOpen ]
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<>
|
|
64
|
+
{ isOpen && (
|
|
65
|
+
<>
|
|
66
|
+
<Modal
|
|
67
|
+
title=""
|
|
68
|
+
contentLabel={ title }
|
|
69
|
+
aria={ {
|
|
70
|
+
labelledby: 'jp-connection__manage-dialog__heading',
|
|
71
|
+
} }
|
|
72
|
+
shouldCloseOnClickOutside={ false }
|
|
73
|
+
shouldCloseOnEsc={ false }
|
|
74
|
+
isDismissible={ false }
|
|
75
|
+
className={ 'jp-connection__manage-dialog' }
|
|
76
|
+
>
|
|
77
|
+
<div className="jp-connection__manage-dialog__content">
|
|
78
|
+
<h1 id="jp-connection__manage-dialog__heading">{ title }</h1>
|
|
79
|
+
<Text className="jp-connection__manage-dialog__large-text">
|
|
80
|
+
{ __(
|
|
81
|
+
'At least one user must be connected for your Jetpack products to work properly.',
|
|
82
|
+
'jetpack'
|
|
83
|
+
) }
|
|
84
|
+
</Text>
|
|
85
|
+
<ManageConnectionActionCard
|
|
86
|
+
title={ __( 'Transfer ownership to another admin', 'jetpack' ) }
|
|
87
|
+
link={ getRedirectUrl( 'calypso-settings-manage-connection', {
|
|
88
|
+
site: window?.myJetpackInitialState?.siteSuffix,
|
|
89
|
+
} ) }
|
|
90
|
+
key="transfer"
|
|
91
|
+
action="transfer"
|
|
92
|
+
/>
|
|
93
|
+
<ManageConnectionActionCard
|
|
94
|
+
title={ __( 'Disconnect Jetpack', 'jetpack' ) }
|
|
95
|
+
onClick={ openDisconnectDialog }
|
|
96
|
+
key="disconnect"
|
|
97
|
+
action="disconnect"
|
|
98
|
+
/>
|
|
99
|
+
</div>
|
|
100
|
+
<HelpFooter onClose={ onClose } />
|
|
101
|
+
</Modal>
|
|
102
|
+
|
|
103
|
+
<DisconnectDialog
|
|
104
|
+
apiRoot={ apiRoot }
|
|
105
|
+
apiNonce={ apiNonce }
|
|
106
|
+
onDisconnected={ onDisconnected }
|
|
107
|
+
connectedPlugins={ connectedPlugins }
|
|
108
|
+
connectedSiteId={ connectedSiteId }
|
|
109
|
+
connectedUser={ connectedUser }
|
|
110
|
+
isOpen={ isDisconnectDialogOpen }
|
|
111
|
+
onClose={ closeDisconnectDialog }
|
|
112
|
+
context={ context }
|
|
113
|
+
/>
|
|
114
|
+
</>
|
|
115
|
+
) }
|
|
116
|
+
</>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const ManageConnectionActionCard = ( { title, onClick = () => null, link = '#', action } ) => {
|
|
121
|
+
return (
|
|
122
|
+
<div className="jp-connection__manage-dialog__action-card card">
|
|
123
|
+
<div className="jp-connection__manage-dialog__action-card__card-content">
|
|
124
|
+
<a
|
|
125
|
+
href={ link }
|
|
126
|
+
className={ classNames(
|
|
127
|
+
'jp-connection__manage-dialog__action-card__card-headline',
|
|
128
|
+
action
|
|
129
|
+
) }
|
|
130
|
+
onClick={ onClick }
|
|
131
|
+
>
|
|
132
|
+
{ title }
|
|
133
|
+
<Icon
|
|
134
|
+
icon={ action === 'disconnect' ? chevronRight : external }
|
|
135
|
+
className="jp-connection__manage-dialog__action-card__icon"
|
|
136
|
+
/>
|
|
137
|
+
</a>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const HelpFooter = ( { onClose } ) => {
|
|
144
|
+
return (
|
|
145
|
+
<div className="jp-row jp-connection__manage-dialog__actions">
|
|
146
|
+
<div className="jp-connection__manage-dialog__text-wrap lg-col-span-9 md-col-span-7 sm-col-span-3">
|
|
147
|
+
<Text>
|
|
148
|
+
{ createInterpolateElement(
|
|
149
|
+
__(
|
|
150
|
+
'<strong>Need help?</strong> Learn more about the <connectionInfoLink>Jetpack connection</connectionInfoLink> or <supportLink>contact Jetpack support</supportLink>',
|
|
151
|
+
'jetpack'
|
|
152
|
+
),
|
|
153
|
+
{
|
|
154
|
+
strong: <strong></strong>,
|
|
155
|
+
connectionInfoLink: (
|
|
156
|
+
<ExternalLink
|
|
157
|
+
href={ getRedirectUrl(
|
|
158
|
+
'why-the-wordpress-com-connection-is-important-for-jetpack'
|
|
159
|
+
) }
|
|
160
|
+
className="jp-connection__manage-dialog__link"
|
|
161
|
+
// TODO add click track
|
|
162
|
+
/>
|
|
163
|
+
),
|
|
164
|
+
supportLink: (
|
|
165
|
+
<ExternalLink
|
|
166
|
+
href={ getRedirectUrl( 'jetpack-support' ) }
|
|
167
|
+
className="jp-connection__manage-dialog__link"
|
|
168
|
+
// TODO add click track
|
|
169
|
+
/>
|
|
170
|
+
),
|
|
171
|
+
}
|
|
172
|
+
) }
|
|
173
|
+
</Text>
|
|
174
|
+
</div>
|
|
175
|
+
<div className="jp-connection__manage-dialog__button-wrap lg-col-span-3 md-col-span-1 sm-col-span-1">
|
|
176
|
+
<Button
|
|
177
|
+
weight="regular"
|
|
178
|
+
variant="secondary"
|
|
179
|
+
onClick={ onClose }
|
|
180
|
+
className="jp-connection__manage-dialog__btn-dismiss"
|
|
181
|
+
>
|
|
182
|
+
{ __( 'Cancel', 'jetpack' ) }
|
|
183
|
+
</Button>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
ManageConnectionDialog.propTypes = {
|
|
190
|
+
/** The modal title. */
|
|
191
|
+
title: PropTypes.string,
|
|
192
|
+
/** API root URL, required. */
|
|
193
|
+
apiRoot: PropTypes.string.isRequired,
|
|
194
|
+
/** API Nonce, required. */
|
|
195
|
+
apiNonce: PropTypes.string.isRequired,
|
|
196
|
+
/** Plugins that are using the Jetpack connection. */
|
|
197
|
+
connectedPlugins: PropTypes.oneOfType( [ PropTypes.array, PropTypes.object ] ),
|
|
198
|
+
/** The callback to be called upon disconnection success. */
|
|
199
|
+
onDisconnected: PropTypes.func,
|
|
200
|
+
/** The context in which this component is being used. */
|
|
201
|
+
context: PropTypes.string,
|
|
202
|
+
/** An object representing the connected user. */
|
|
203
|
+
connectedUser: PropTypes.object,
|
|
204
|
+
/** ID of the currently connected site. */
|
|
205
|
+
connectedSiteId: PropTypes.number,
|
|
206
|
+
/** Whether or not the dialog modal should be open. */
|
|
207
|
+
isOpen: PropTypes.bool,
|
|
208
|
+
/** Callback function for when the modal closes. */
|
|
209
|
+
onClose: PropTypes.func,
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
ManageConnectionDialog.defaultProps = {
|
|
213
|
+
title: __( 'Manage your Jetpack connection', 'jetpack' ),
|
|
214
|
+
isOpen: false,
|
|
215
|
+
context: 'jetpack-dashboard',
|
|
216
|
+
connectedUser: {}, // Pass empty object to avoid undefined errors.
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
export default ManageConnectionDialog;
|