PluginProbe
BeyondWords – AI audio for publishers / 7.2.0
BeyondWords – AI audio for publishers v7.2.0
7.2.0 7.1.0 trunk 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.0.5 4.0.6 4.1.0 4.1.1 4.1.2 4.2.0 4.2.1 4.2.2 4.2.3 4.2.4 4.3.0 4.4.0 4.5.0 4.5.1 4.6.0 4.6.1 4.6.2 All 44 releases
speechkit / src / editor / components / block-attributes / addControls.js

addControls.js in BeyondWords – AI audio for publishers 7.2.0, at src/editor/components/block-attributes/addControls.js

188 lines 5.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * WordPress Dependencies
3 */
4 import { __ } from '@wordpress/i18n';
5 import { InspectorControls, BlockControls } from '@wordpress/block-editor';
6 import { PanelBody, ToolbarButton, ToolbarGroup } from '@wordpress/components';
7 import { createHigherOrderComponent } from '@wordpress/compose';
8 import { useSelect } from '@wordpress/data';
9 import { useState } from '@wordpress/element';
10 import { addFilter } from '@wordpress/hooks';
11
12 /**
13 * Internal dependencies
14 */
15 import { isBeyondwordsSupportedBlock } from './isBeyondwordsSupportedBlock';
16 import Stack from '../stack';
17 import Toggle from '../toggle';
18 import VoicePicker from '../voice-picker';
19 import { BeyondwordsTitle } from '../icon';
20
21 /**
22 * Add BeyondWords controls to Gutenberg Blocks.
23 *
24 * @since 6.0.1 Skip internal/UI blocks to prevent breaking the block inserter.
25 * @since 7.1.0 Add the per-block Language and Voice pickers.
26 *
27 * @param {Function} BlockEdit Block edit component.
28 *
29 * @return {Function} BlockEdit Modified block edit component.
30 */
31 const withBeyondwordsBlockControls = createHigherOrderComponent(
32 ( BlockEdit ) => {
33 // A component of its own, so the unsupported-block check can return
34 // before any hook runs.
35 const BeyondwordsControls = ( props ) => {
36 const { attributes, setAttributes } = props;
37 const {
38 beyondwordsAudio,
39 beyondwordsLanguageCode,
40 beyondwordsVoiceId,
41 } = attributes;
42
43 // "Customize" is opt-in: a block counts as customised once it carries
44 // an explicit language or voice.
45 const [ customize, setCustomize ] = useState(
46 () => !! ( beyondwordsLanguageCode || beyondwordsVoiceId )
47 );
48
49 // What this block already reads aloud with: the post's own choice
50 // where it has one, otherwise the project's. Fetched only once
51 // Customize is on, so an untouched block still costs no requests.
52 const seed = useSelect(
53 ( select ) => {
54 const meta =
55 select( 'core/editor' ).getEditedPostAttribute(
56 'meta'
57 ) || {};
58
59 if ( meta.beyondwords_language_code ) {
60 return {
61 languageCode: meta.beyondwords_language_code,
62 voiceId: meta.beyondwords_body_voice_id || '',
63 };
64 }
65
66 const projectId =
67 meta.beyondwords_project_id ||
68 select( 'beyondwords/settings' ).getSettings()
69 ?.projectId;
70
71 const project =
72 customize && projectId
73 ? select( 'beyondwords/settings' ).getProject(
74 projectId
75 )
76 : null;
77
78 return {
79 languageCode: project?.language || '',
80 voiceId: String( project?.body?.voice?.id ?? '' ),
81 };
82 },
83 [ customize ]
84 );
85
86 const icon = beyondwordsAudio
87 ? 'controls-volumeon'
88 : 'controls-volumeoff';
89
90 const buttonLabel = beyondwordsAudio
91 ? __( 'Disable generation', 'speechkit' )
92 : __( 'Enable generation', 'speechkit' );
93
94 const toggleLabel = beyondwordsAudio
95 ? __( 'Generation enabled', 'speechkit' )
96 : __( 'Generation disabled', 'speechkit' );
97
98 const toggleBeyondwordsAudio = () => {
99 setAttributes( { beyondwordsAudio: ! beyondwordsAudio } );
100 };
101
102 const setVoice = ( { languageCode, voiceId } ) => {
103 setAttributes( {
104 beyondwordsLanguageCode: languageCode,
105 beyondwordsVoiceId: voiceId,
106 } );
107 };
108
109 // Customize off returns the block to the post's language and voice.
110 const toggleCustomize = () => {
111 const next = ! customize;
112 setCustomize( next );
113
114 if ( ! next ) {
115 setVoice( { languageCode: '', voiceId: '' } );
116 }
117 };
118
119 return (
120 <>
121 <BlockEdit { ...props } />
122
123 <InspectorControls>
124 <PanelBody
125 className="beyondwords--block-settings"
126 title={ <BeyondwordsTitle /> }
127 initialOpen={ true }
128 >
129 <Stack>
130 <Toggle
131 label={ toggleLabel }
132 checked={ !! beyondwordsAudio }
133 onChange={ toggleBeyondwordsAudio }
134 />
135 { !! beyondwordsAudio && (
136 <Toggle
137 className="beyondwords--customize-block"
138 label={ __( 'Customize', 'speechkit' ) }
139 checked={ customize }
140 onChange={ toggleCustomize }
141 />
142 ) }
143 { !! beyondwordsAudio && (
144 <VoicePicker
145 enabled={ customize }
146 seed={ seed }
147 languageCode={
148 beyondwordsLanguageCode || ''
149 }
150 voiceId={ beyondwordsVoiceId || '' }
151 onChange={ setVoice }
152 />
153 ) }
154 </Stack>
155 </PanelBody>
156 </InspectorControls>
157
158 <BlockControls>
159 <ToolbarGroup>
160 <ToolbarButton
161 icon={ icon }
162 label={ buttonLabel }
163 className="components-toolbar__control"
164 onClick={ toggleBeyondwordsAudio }
165 />
166 </ToolbarGroup>
167 </BlockControls>
168 </>
169 );
170 };
171
172 return ( props ) => {
173 if ( ! isBeyondwordsSupportedBlock( props.name ) ) {
174 return <BlockEdit { ...props } />;
175 }
176
177 return <BeyondwordsControls { ...props } />;
178 };
179 },
180 'withBeyondwordsBlockControls'
181 );
182
183 addFilter(
184 'editor.BlockEdit',
185 'beyondwords/block-controls',
186 withBeyondwordsBlockControls
187 );
188