Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 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
2 changes: 1 addition & 1 deletion ui/js/blocks/pods-blocks-api.min.asset.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"dependencies":["lodash","react","react-dom","wp-api-fetch","wp-autop","wp-block-editor","wp-blocks","wp-components","wp-compose","wp-date","wp-element","wp-i18n","wp-keycodes","wp-server-side-render","wp-url"],"version":"559be62f189fbb2870a7"}
{"dependencies":["lodash","react","react-dom","wp-api-fetch","wp-autop","wp-block-editor","wp-blocks","wp-components","wp-compose","wp-date","wp-element","wp-i18n","wp-keycodes","wp-server-side-render","wp-url"],"version":"455167998375a626b9ff"}
2 changes: 1 addition & 1 deletion ui/js/blocks/pods-blocks-api.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion ui/js/dfv/pods-dfv.min.asset.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"dependencies":["lodash","moment","react","react-dom","react-jsx-runtime","regenerator-runtime","wp-api-fetch","wp-autop","wp-components","wp-compose","wp-data","wp-element","wp-hooks","wp-i18n","wp-keycodes","wp-plugins","wp-primitives","wp-url"],"version":"073a6a8b184586b2e522"}
{"dependencies":["lodash","moment","react","react-dom","react-jsx-runtime","regenerator-runtime","wp-api-fetch","wp-autop","wp-components","wp-compose","wp-data","wp-element","wp-hooks","wp-i18n","wp-keycodes","wp-plugins","wp-primitives","wp-url"],"version":"59e529b75cb13c99576f"}
2 changes: 1 addition & 1 deletion ui/js/dfv/pods-dfv.min.js

Large diffs are not rendered by default.

22 changes: 19 additions & 3 deletions ui/js/dfv/src/components/field-wrapper/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import React, { useRef, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { isEqual, uniq } from 'lodash';
import PropTypes from 'prop-types';

Expand Down Expand Up @@ -36,6 +36,7 @@ import { FIELD_PROP_TYPE_SHAPE } from 'dfv/src/config/prop-types';

import './field-wrapper.scss';
import RepeatableFieldList from './repeatable-field-list';
import useBlockEditor from '../../hooks/useBlockEditor';

export const FieldWrapper = ( props ) => {
const {
Expand Down Expand Up @@ -192,6 +193,21 @@ export const FieldWrapper = ( props ) => {
value
);

// Handle Block Editor save lock.
const blockEditor = useBlockEditor();
useEffect( () => {
if ( ! meetsConditionalLogic || ! validationMessages.length ) {
blockEditor.unlockPostSaving( `pods-field-${ name }` );
} else {
blockEditor.lockPostSaving( `pods-field-${ name }`, validationMessages, () => setHasBlurred( true ) );
}

// Unlock on unmount.
return () => {
blockEditor.unlockPostSaving( `pods-field-${ name }` );
};
}, [ validationMessages, meetsConditionalLogic ] );

// Don't render a field that hasn't had its dependencies met.
if ( ! meetsConditionalLogic ) {
return <span ref={ fieldRef } />;
Expand Down Expand Up @@ -225,7 +241,7 @@ export const FieldWrapper = ( props ) => {
allPodValues={ passAllPodValues ? allPodValues : undefined }
allPodFieldsMap={ passAllPodFieldsMap ? allPodFieldsMap : undefined }
setOptionValue={ setOptionValue }
isValid={ !! validationMessages.length }
isValid={ ! validationMessages.length }
addValidationRules={ addValidationRules }
setHasBlurred={ () => setHasBlurred( true ) }
fieldConfig={ field }
Expand Down Expand Up @@ -259,7 +275,7 @@ export const FieldWrapper = ( props ) => {
allPodValues={ allPodValues }
allPodFieldsMap={ allPodFieldsMap }
setValue={ setValue }
isValid={ !! validationMessages.length }
isValid={ ! validationMessages.length }
addValidationRules={ addValidationRules }
setHasBlurred={ () => setHasBlurred( true ) }
fieldConfig={ processedFieldConfig }
Expand Down
4 changes: 4 additions & 0 deletions ui/js/dfv/src/core/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@ import ConnectedFieldWrapper from 'dfv/src/components/connected-field-wrapper';
* Pods config
*/
import { FIELD_PROP_TYPE_SHAPE } from 'dfv/src/config/prop-types';
import useBlockEditor from '../hooks/useBlockEditor';

const App = ( {
storeKey,
} ) => {
const fieldsData = PodsDFVAPI._fieldDataByStoreKeyPrefix[ storeKey ];
const allPodFieldsMap = new Map( fieldsData.map( ( field ) => [ field.name, field ] ) );

// Initialize Pods Block Editor overrides if available.
useBlockEditor();

const fieldComponents = fieldsData.map( ( fieldData = {} ) => {
const {
directRender = false,
Expand Down
91 changes: 91 additions & 0 deletions ui/js/dfv/src/hooks/useBlockEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@

/**
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';

const useBlockEditor = () => {
const editorSelect = wp.data?.select( 'core/editor' );
const editorDispatch = wp.data?.dispatch( 'core/editor' );
const notices = wp.data?.dispatch( 'core/notices' );

// @todo Use hook instead of savePost override once stable.
if ( ! window.PodsBlockEditor && editorDispatch && editorDispatch.hasOwnProperty( 'savePost' ) ) {
// First init.
window.PodsBlockEditor = {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optionally make this a class with private props and getters/setters. Maybe even an external tool like the API instead of a React hook.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets keep it like this since we should definitely change this code once a hook is available.
The added todo will inform any dev checking this code in that it's a temp solution.

// Store original.
savePost: editorDispatch.savePost,
messages: {},
callbacks: {},
};

// Override the current editor savePost function.
editorDispatch.savePost = async ( options ) => {
options = options || {};

const pbe = window.PodsBlockEditor;

if ( ! Object.values( pbe.messages ).length ) {
// eslint-disable-next-line no-undef
return pbe.savePost.apply( this, arguments );
}

return new Promise( function( resolve, reject ) {
// Bail early if is autosave or preview.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sc0ttkclark Should we allow saving for a draft?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm on the fence here. Maybe we can make that an option in settings or a constant/hook. Default to allow saves on drafts perhaps.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But there's no way to draft by default when you have a post that's already published. So we'd need to distinguish between save and save draft.

Copy link
Member Author

@JoryHogeveen JoryHogeveen Oct 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also leave it as-is and add it later if you want. I can quite easily fetch the status value and check that, however, it's hard to define what statuses should be allowed or not.
For ease of use (and clarity) it might be better to just validate by default for now.

EDIT: example

const status = editorSelect.getEditedPostAttribute('status');
if ( [ 'draft', 'trash', 'otherStatus' ].includes( status ) ) {
    // Run default
}
// OR
if ( SOME_USER_SETTING_ARRAY.includes( status ) ) {
    // Run default
}

if ( options.isAutosave || options.isPreview ) {
return resolve( 'Validation ignored (autosave).' );
}

let validationFieldErrorList = [];
for ( const fieldName in pbe.messages ) {
editorDispatch?.lockPostSaving( fieldName );

let fieldRealName = fieldName.replace( 'pods-field-', '' );
let fieldData = window.PodsDFVAPI.getField(fieldRealName);

validationFieldErrorList.push(fieldData?.fieldConfig?.label ?? realFieldName);
}

notices.createErrorNotice(
sprintf(__('Please complete these fields before saving: %s', 'pods'), validationFieldErrorList.join(', ')),
{id: 'pods-validation', isDismissible: true},
);

for ( const fieldCallback in pbe.callbacks ) {
if ( pbe.callbacks.hasOwnProperty( fieldCallback ) && 'function' === typeof pbe.callbacks[ fieldCallback ] ) {
pbe.callbacks[ fieldCallback ]();
}
}

return reject( 'Pods validation failed' );
} );
};
}

return {
data: wp.data,
select: editorSelect,
dispatch: editorDispatch,
notices,
lockPostSaving: ( name, messages, callback ) => {
// @todo Use hook instead of savePost override once stable.
//wp.hooks.addFilter( 'editor.__unstablePreSavePost', 'editor', filter );

const pbe = window.PodsBlockEditor;
if ( messages.length ) {
pbe.messages[ name ] = messages;
pbe.callbacks[ name ] = callback;
}
},
unlockPostSaving: ( name ) => {
// @todo Use hook instead of savePost override once stable.
//wp.hooks.removeFilter( 'editor.__unstablePreSavePost', 'editor', filter );

delete window.PodsBlockEditor.messages[ name ];
delete window.PodsBlockEditor.callbacks[ name ];
editorDispatch?.unlockPostSaving( name );
},
};
};

export default useBlockEditor;
Loading