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

WIP attempt at making shared blocks behave like any ordinary CPT #7739

Closed
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
39 changes: 37 additions & 2 deletions core-blocks/block/edit-panel/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
/**
* External dependencies
*/
import { over, compact } from 'lodash';

/**
* WordPress dependencies
*/
import { Button, withInstanceId } from '@wordpress/components';
import { Component, Fragment, createRef } from '@wordpress/element';
import { Component, Fragment, createRef, compose } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { keycodes } from '@wordpress/utils';
import { withSelect, withDispatch } from '@wordpress/data';

/**
* Internal dependencies
Expand Down Expand Up @@ -117,4 +123,33 @@ class SharedBlockEditPanel extends Component {
}
}

export default withInstanceId( SharedBlockEditPanel );
export default compose( [
withInstanceId,
withSelect( ( select ) => {
const { getEditedPostAttribute } = select( 'core/editor' );

return {
title: getEditedPostAttribute( 'title' ),
};
} ),
withDispatch( ( dispatch, ownProps ) => {
const {
editPost,
undoAll,
savePost,
clearSelectedBlock,
} = dispatch( 'core/editor' );

const withClearAndFinish = ( fn ) => over( compact( [
clearSelectedBlock,
ownProps.onFinishedEditing,
fn,
] ) );

return {
onChangeTitle: ( title ) => editPost( { title } ),
onSave: withClearAndFinish( savePost ),
onCancel: withClearAndFinish( undoAll ),
};
} ),
] )( SharedBlockEditPanel );
174 changes: 58 additions & 116 deletions core-blocks/block/edit.js
Original file line number Diff line number Diff line change
@@ -1,169 +1,111 @@
/**
* External dependencies
*/
import { noop, partial } from 'lodash';

/**
* WordPress dependencies
*/
import { Component, Fragment, compose } from '@wordpress/element';
import { Component, compose } from '@wordpress/element';
import { Placeholder, Spinner, Disabled } from '@wordpress/components';
import { withSelect, withDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { BlockEdit } from '@wordpress/editor';
import { EditorProvider, BlockList } from '@wordpress/editor';
import isShallowEqual from '@wordpress/is-shallow-equal';

/**
* Internal dependencies
*/
import SharedBlockEditPanel from './edit-panel';
import SharedBlockIndicator from './indicator';
import SharedBlockSelection from './selection';

class SharedBlockEdit extends Component {
constructor( { sharedBlock } ) {
constructor( props ) {
super( ...arguments );

this.startEditing = this.startEditing.bind( this );
this.stopEditing = this.stopEditing.bind( this );
this.setAttributes = this.setAttributes.bind( this );
this.setTitle = this.setTitle.bind( this );
this.save = this.save.bind( this );
this.startEditing = this.toggleEditing.bind( this, true );
this.stopEditing = this.toggleEditing.bind( this, false );

const { sharedBlock, settings } = props;
this.state = {
isEditing: !! ( sharedBlock && sharedBlock.isTemporary ),
title: null,
changedAttributes: null,
settingsWithLock: { ...settings, templateLock: true },
};
}

componentDidMount() {
if ( ! this.props.sharedBlock ) {
this.props.fetchSharedBlock();
static getDerivedStateFromProps( props, prevState ) {
if ( isShallowEqual( props.settings, prevState.settings ) ) {
return null;
}
}

startEditing() {
const { sharedBlock } = this.props;

this.setState( {
isEditing: true,
title: sharedBlock.title,
changedAttributes: {},
} );
}

stopEditing() {
this.setState( {
isEditing: false,
title: null,
changedAttributes: null,
} );
}

setAttributes( attributes ) {
this.setState( ( prevState ) => {
if ( prevState.changedAttributes !== null ) {
return { changedAttributes: { ...prevState.changedAttributes, ...attributes } };
}
} );
}

setTitle( title ) {
this.setState( { title } );
return {
settings: props.settings,
settingsWithLock: {
...props.settings,
templateLock: true,
},
};
}

save() {
const { sharedBlock, onUpdateTitle, updateAttributes, block, onSave } = this.props;
const { title, changedAttributes } = this.state;

if ( title !== sharedBlock.title ) {
onUpdateTitle( title );
}

updateAttributes( block.uid, changedAttributes );
onSave();

this.stopEditing();
toggleEditing( isEditing ) {
this.setState( { isEditing } );
}

render() {
const { isSelected, sharedBlock, block, isFetching, isSaving } = this.props;
const { isEditing, title, changedAttributes } = this.state;
const { setIsSelected, sharedBlock, isSelected, isSaving } = this.props;
const { settingsWithLock, isEditing } = this.state;

if ( ! sharedBlock && isFetching ) {
if ( ! sharedBlock ) {
return <Placeholder><Spinner /></Placeholder>;
}

if ( ! sharedBlock || ! block ) {
return <Placeholder>{ __( 'Block has been deleted or is unavailable.' ) }</Placeholder>;
}

let element = (
<BlockEdit
{ ...this.props }
isSelected={ isEditing && isSelected }
id={ block.uid }
name={ block.name }
attributes={ { ...block.attributes, ...changedAttributes } }
setAttributes={ isEditing ? this.setAttributes : noop }
/>
);

let list = <BlockList />;
if ( ! isEditing ) {
element = <Disabled>{ element }</Disabled>;
list = <Disabled>{ list }</Disabled>;
}

return (
<Fragment>
{ element }
{ ( isSelected || isEditing ) && (
<SharedBlockEditPanel
isEditing={ isEditing }
title={ title !== null ? title : sharedBlock.title }
isSaving={ isSaving && ! sharedBlock.isTemporary }
onEdit={ this.startEditing }
onChangeTitle={ this.setTitle }
onSave={ this.save }
onCancel={ this.stopEditing }
/>
) }
{ ! isSelected && ! isEditing && <SharedBlockIndicator title={ sharedBlock.title } /> }
</Fragment>
<EditorProvider
reducerKey={ 'core/editor-shared-' + sharedBlock.id }
postType="wp_block"
inheritContext
settings={ settingsWithLock }
post={ sharedBlock }
>
<SharedBlockSelection
isSharedBlockSelected={ isSelected }
onBlockSelection={ setIsSelected }
>
{ list }
{ ( isSelected || isEditing ) && (
<SharedBlockEditPanel
isEditing={ isEditing }
isSaving={ isSaving && ! sharedBlock.isTemporary }
onEdit={ this.startEditing }
onFinishedEditing={ this.stopEditing }
/>
) }
{ ! isSelected && ! isEditing && <SharedBlockIndicator /> }
</SharedBlockSelection>
</EditorProvider>
);
}
}

export default compose( [
withSelect( ( select, ownProps ) => {
const {
getSharedBlock,
isFetchingSharedBlock,
isSavingSharedBlock,
getBlock,
} = select( 'core/editor' );
const { ref } = ownProps.attributes;
const sharedBlock = getSharedBlock( ref );
if ( ! Number.isFinite( ref ) ) {
return;
}

const { getEntityRecord } = select( 'core' );
return {
sharedBlock,
isFetching: isFetchingSharedBlock( ref ),
isSaving: isSavingSharedBlock( ref ),
block: sharedBlock ? getBlock( sharedBlock.uid ) : null,
sharedBlock: getEntityRecord( 'postType', 'wp_block', ref ),
settings: select( 'core/editor' ).getEditorSettings(),
};
} ),
withDispatch( ( dispatch, ownProps ) => {
const {
fetchSharedBlocks,
updateBlockAttributes,
updateSharedBlockTitle,
saveSharedBlock,
} = dispatch( 'core/editor' );
const { ref } = ownProps.attributes;
const { selectBlock } = dispatch( 'core/editor' );
const { id } = ownProps;

return {
fetchSharedBlock: partial( fetchSharedBlocks, ref ),
updateAttributes: updateBlockAttributes,
onUpdateTitle: partial( updateSharedBlockTitle, ref ),
onSave: partial( saveSharedBlock, ref ),
setIsSelected: () => selectBlock( id ),
};
} ),
] )( SharedBlockEdit );
23 changes: 18 additions & 5 deletions core-blocks/block/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,31 @@ function render_block_core_block( $attributes ) {
}

$shared_block = get_post( $attributes['ref'] );
if ( ! $shared_block || 'wp_block' !== $shared_block->post_type ) {
if ( ! $shared_block ) {
return '';
}

$blocks = gutenberg_parse_blocks( $shared_block->post_content );
if ( 'wp_block' !== $shared_block->post_type ) {
return '';
}

$block = array_shift( $blocks );
if ( ! $block ) {
// TODO: Does this correctly handle scheduled blocks?
if ( 'publish' !== $shared_block->post_status ) {
return '';
}

return gutenberg_render_block( $block );
// TODO: Not sure how to support this
if ( $shared_block->post_password ) {
return '';
}

$blocks = gutenberg_parse_blocks( $shared_block->post_content );

$html = '';
foreach ( $blocks as $block ) {
$html .= gutenberg_render_block( $block );
}
return $html;
}

register_block_type( 'core/block', array(
Expand Down
9 changes: 8 additions & 1 deletion core-blocks/block/indicator/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import { Tooltip, Dashicon } from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import { withSelect } from '@wordpress/data';

/**
* Internal dependencies
Expand All @@ -21,4 +22,10 @@ function SharedBlockIndicator( { title } ) {
);
}

export default SharedBlockIndicator;
export default withSelect( ( select ) => {
const { getEditedPostAttribute } = select( 'core/editor' );

return {
title: getEditedPostAttribute( 'title' ),
};
} )( SharedBlockIndicator );
42 changes: 42 additions & 0 deletions core-blocks/block/selection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* WordPress dependencies
*/
import { Component, compose } from '@wordpress/element';
import { withSelect, withDispatch } from '@wordpress/data';

class SharedBlockSelection extends Component {
componentDidUpdate( prevProps ) {
const {
isSharedBlockSelected,
hasSelection,
clearSelectedBlock,
onBlockSelection,
} = this.props;

if ( ! isSharedBlockSelected && prevProps.isSharedBlockSelected ) {
clearSelectedBlock();
}

if ( hasSelection && ! prevProps.hasSelection ) {
onBlockSelection();
}
}

render() {
return this.props.children;
}
}

export default compose( [
withSelect( ( select ) => {
const { getBlockSelectionStart } = select( 'core/editor' );

return {
hasSelection: !! getBlockSelectionStart(),
};
} ),
withDispatch( ( dispatch ) => {
const { clearSelectedBlock } = dispatch( 'core/editor' );
return { clearSelectedBlock };
} ),
] )( SharedBlockSelection );
Loading