PluginProbe ʕ •ᴥ•ʔ
Public Post Preview / trunk
Public Post Preview vtrunk
trunk 1.0 1.1 1.2 1.3 2.0 2.0.1 2.1 2.1.1 2.10.0 2.2 2.2-beta 2.3 2.4 2.4.1 2.5.0 2.6.0 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.2
public-post-preview / js / src / components / preview-toggle / index.js
public-post-preview / js / src / components / preview-toggle Last commit date
index.js 4 months ago
index.js
263 lines
1 /**
2 * External dependencies
3 */
4 import { css } from '@emotion/css';
5
6 /**
7 * WordPress dependencies
8 */
9 import {
10 Button,
11 CheckboxControl,
12 ExternalLink,
13 TextControl,
14 VisuallyHidden,
15 } from '@wordpress/components';
16 import { compose, ifCondition, useCopyToClipboard } from '@wordpress/compose';
17 import { store as coreStore } from '@wordpress/core-data';
18 import { useDispatch, withDispatch, withSelect } from '@wordpress/data';
19 import {
20 store as editorStore,
21 PluginPostStatusInfo,
22 PluginPreviewMenuItem,
23 } from '@wordpress/editor';
24 import { Component, createInterpolateElement, createRef } from '@wordpress/element';
25 import { __ } from '@wordpress/i18n';
26 import { copySmall, seen } from '@wordpress/icons';
27 import { store as noticesStore } from '@wordpress/notices';
28
29 const { ajaxurl, DSPublicPostPreviewData } = window;
30
31 const pluginPostStatusInfoRow = css`
32 flex-direction: column;
33 align-items: flex-start;
34 `;
35
36 const pluginPostStatusInfoPreviewUrl = css`
37 margin-top: 8px;
38 width: 100%;
39 `;
40
41 const pluginPostStatusInfoPreviewDescription = css`
42 color: #757575;
43 margin: 8px 0 0 !important;
44 `;
45
46 const pluginPostStatusInfoPreviewUrlInputWrapper = css`
47 position: relative;
48 display: flex;
49 justify-content: flex-start;
50 align-items: center;
51
52 .components-base-control {
53 width: 100%;
54 }
55 `;
56
57 const pluginPostStatusInfoPreviewUrlInput = css`
58 .components-text-control__input {
59 background-color: #fff;
60 padding-right: 30px !important;
61 }
62 `;
63
64 const pluginPostStatusInfoPreviewCheckbox = css`
65 label {
66 max-width: 100%;
67 }
68 `;
69
70 const copyButton = css`
71 position: absolute;
72 right: 5px;
73 top; 0;
74 `;
75
76 function CopyButton( { text } ) {
77 const { createNotice } = useDispatch( noticesStore );
78 const ref = useCopyToClipboard( text, () => {
79 createNotice( 'info', __( 'Preview link copied to clipboard.', 'public-post-preview' ), {
80 isDismissible: true,
81 type: 'snackbar',
82 } );
83 } );
84 return (
85 <Button
86 icon={ copySmall }
87 ref={ ref }
88 label={ __( 'Copy the preview URL', 'public-post-preview' ) }
89 className={ copyButton }
90 size="small"
91 />
92 );
93 }
94
95 function PreviewMenuItem( { previewUrl } ) {
96 if ( 'function' !== typeof PluginPreviewMenuItem || ! previewUrl ) {
97 return null;
98 }
99
100 return (
101 <PluginPreviewMenuItem icon={ seen } href={ previewUrl } target="_blank">
102 { __( 'Open public preview', 'public-post-preview' ) }
103 <VisuallyHidden as="span">
104 {
105 /* translators: accessibility text */
106 __( '(opens in a new tab)', 'public-post-preview' )
107 }
108 </VisuallyHidden>
109 </PluginPreviewMenuItem>
110 );
111 }
112
113 class PreviewToggle extends Component {
114 constructor( props ) {
115 super( props );
116
117 this.state = {
118 previewEnabled: DSPublicPostPreviewData.previewEnabled,
119 previewUrl: DSPublicPostPreviewData.previewUrl,
120 };
121
122 this.previewUrlInput = createRef();
123
124 this.onChange = this.onChange.bind( this );
125 this.onPreviewUrlInputFocus = this.onPreviewUrlInputFocus.bind( this );
126 }
127
128 onChange( checked ) {
129 const data = new window.FormData();
130 data.append( 'checked', checked );
131 data.append( 'post_ID', this.props.postId );
132
133 this.sendRequest( data )
134 .then( ( response ) => {
135 if ( response.status >= 200 && response.status < 300 ) {
136 return response;
137 }
138
139 throw response;
140 } )
141 .then( ( response ) => response.json() )
142 .then( ( response ) => {
143 if ( ! response.success ) {
144 throw response;
145 }
146
147 const previewEnabled = ! this.state.previewEnabled;
148 this.setState( {
149 previewEnabled,
150 previewUrl: response?.data?.preview_url || '',
151 } );
152
153 this.props.createNotice(
154 'info',
155 previewEnabled
156 ? __( 'Public preview enabled.', 'public-post-preview' )
157 : __( 'Public preview disabled.', 'public-post-preview' ),
158 {
159 id: 'public-post-preview',
160 isDismissible: true,
161 type: 'snackbar',
162 }
163 );
164 } )
165 .catch( () => {
166 this.props.createNotice(
167 'error',
168 __( 'Error while changing the public preview status.', 'public-post-preview' ),
169 {
170 id: 'public-post-preview',
171 isDismissible: true,
172 type: 'snackbar',
173 }
174 );
175 } );
176 }
177
178 onPreviewUrlInputFocus() {
179 this.previewUrlInput.current.focus();
180 this.previewUrlInput.current.select();
181 }
182
183 sendRequest( data ) {
184 data.append( 'action', 'public-post-preview' );
185 data.append( '_ajax_nonce', DSPublicPostPreviewData.nonce );
186 return window.fetch( ajaxurl, {
187 method: 'POST',
188 body: data,
189 } );
190 }
191
192 render() {
193 const { previewEnabled, previewUrl } = this.state;
194
195 return (
196 <>
197 <PreviewMenuItem previewUrl={ previewEnabled && previewUrl ? previewUrl : null } />
198 <PluginPostStatusInfo className={ pluginPostStatusInfoRow }>
199 <CheckboxControl
200 label={ __( 'Enable public preview', 'public-post-preview' ) }
201 checked={ previewEnabled }
202 onChange={ this.onChange }
203 className={ pluginPostStatusInfoPreviewCheckbox }
204 __nextHasNoMarginBottom
205 />
206 { previewEnabled && (
207 <div className={ pluginPostStatusInfoPreviewUrl }>
208 <div className={ pluginPostStatusInfoPreviewUrlInputWrapper }>
209 <TextControl
210 ref={ this.previewUrlInput }
211 hideLabelFromVision
212 label={ __( 'Preview URL', 'public-post-preview' ) }
213 value={ previewUrl }
214 readOnly
215 onFocus={ this.onPreviewUrlInputFocus }
216 className={ pluginPostStatusInfoPreviewUrlInput }
217 __next40pxDefaultSize
218 __nextHasNoMarginBottom
219 />
220 <CopyButton text={ previewUrl } />
221 </div>
222 <p className={ pluginPostStatusInfoPreviewDescription }>
223 { createInterpolateElement(
224 __(
225 'Copy and share <a>the preview URL</a>.',
226 'public-post-preview'
227 ),
228 {
229 a: <ExternalLink href={ previewUrl } />,
230 }
231 ) }
232 </p>
233 </div>
234 ) }
235 </PluginPostStatusInfo>
236 </>
237 );
238 }
239 }
240
241 export default compose( [
242 withSelect( ( select ) => {
243 const { getPostType } = select( coreStore );
244 const { getCurrentPostId, getEditedPostAttribute } = select( editorStore );
245 const postType = getPostType( getEditedPostAttribute( 'type' ) );
246
247 return {
248 postId: getCurrentPostId(),
249 status: getEditedPostAttribute( 'status' ),
250 isViewable: postType?.viewable || false,
251 };
252 } ),
253 ifCondition( ( { isViewable } ) => isViewable ),
254 ifCondition( ( { status } ) => {
255 return [ 'auto-draft', 'publish', 'private' ].indexOf( status ) === -1;
256 } ),
257 withDispatch( ( dispatch ) => {
258 return {
259 createNotice: dispatch( noticesStore ).createNotice,
260 };
261 } ),
262 ] )( PreviewToggle );
263