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

Stop trying to autosave when title and classic block content both are empty. #10404

Conversation

rahulsprajapati
Copy link
Contributor

Description

Fixes: #6556

Add check in getBlocksForSerialization when single classic block ( core/freeform ) is available with empty content.

export function isEditedPostEmpty( state ) {
// While the condition of truthy content string would be sufficient for
// determining emptiness, testing saveable blocks length is a trivial
// operation by comparison. Since this function can be called frequently,
// optimize for the fast case where saveable blocks are non-empty.
return (
! getBlocksForSerialization( state ).length &&
! getEditedPostAttribute( state, 'content' )
);
}

How has this been tested?

Post autosave is not triggerd when one classic block content is availble and it's empty.
Screencast: https://drive.google.com/file/d/1p6HfNi7I6_57y8mbLhe9J8HTU4lAq--6/view

Screenshots

Types of changes

Bug fix (non-breaking change which fixes an issue)

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • My code has proper inline documentation.

@danielbachhuber danielbachhuber added this to the 4.1 milestone Oct 9, 2018
Copy link
Member

@danielbachhuber danielbachhuber left a comment

Choose a reason for hiding this comment

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

👍 I've tested this locally and confirm autosave doesn't run until some text is entered in the post content.

I'll defer to @aduth or @youknowriad on the underlying JavaScript

@danielbachhuber danielbachhuber added [Type] Bug An existing feature does not function as intended Backwards Compatibility Issues or PRs that impact backwards compatability [Feature] Saving Related to saving functionality labels Oct 9, 2018
Copy link
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

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

My suggestion noted at #6556 (comment) would avoid the need to inspect specific attributes of the block, rather relying on the existing serializer behavior to be performed, testing the result of which as being non-empty to determine whether a save can occur.

Pseudo-code:

const isEditedPostEmpty = ( state ) => ! hasKnownBlock( state ) && ! getEditedPostAttribute( state, 'content' ) 

const hasKnownBlock = ( state ) => getBlockOrder( state ).some( ( clientId ) => (
	state.editor.current.blocksByClientId[ clientId ].name !== getUnknownTypeHandlerName()
) );

(In second function, I avoid getBlocks because it has a non-trivial cost to execute)

const isSingleEmptyFreeformBlock = (
blocks.length === 1 &&
blocks[ 0 ].name === 'core/freeform' &&
( isEmpty( blocks[ 0 ].attributes ) || blocks[ 0 ].attributes.content === '' )
Copy link
Member

Choose a reason for hiding this comment

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

This pierces the abstraction of blocks by duplicating the save implementation of the freeform block between block-library and editor. Something like the above isUnmodifiedDefaultBlock (in blocks) is a small improvement.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hey, I think I'm confused about this. Can you give me more( simple ) detail?
Sorry, Gutenberg and react is still new for me so I might not be doing this right.

Copy link
Member

Choose a reason for hiding this comment

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

Sorry for being unclear. In short: We shouldn't pretend to know anything about what attributes do or don't exist here, because it will make our lives more difficult later if we ever need to change the attributes.

It's not entirely relevant if we consider the recommendation at #10404 (review)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, yeah sure. Got it we should not use specific attribute name from a direct object and dependent on that. Also, this one is just checking for one empty block there might be more so it would be better if we check in all blocks in the current state. #10404 (review)

I'll update this occordingly.

// A single empty core/freeform block equivalent to an empty post.
const isSingleEmptyFreeformBlock = (
blocks.length === 1 &&
blocks[ 0 ].name === 'core/freeform' &&
Copy link
Member

Choose a reason for hiding this comment

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

The fallback block for which comment demarcation is omitted is not guaranteed to be explicitly 'core/freeform'.

See:

switch ( blockName ) {
case getUnknownTypeHandlerName():
return saveContent;
default:
return getCommentDelimitedContent( blockName, saveAttributes, saveContent );
}

As in this snippet, it should be tested against the value returned by getUnknownTypeHandlerName.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated this with getUnknownTypeHandlerName,

@rahulsprajapati
Copy link
Contributor Author

Sure @aduth, I'll look into this feedbacks and update this PR soon.

@danielbachhuber
Copy link
Member

@rahulsprajapati Any chance you can finish this up Thursday (10/17) IST so we can get this landed?

Copy link
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

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

As of #8274, getUnknownTypeHandlerName has been replaced by two functions getFreeformFallbackBlockName and getUnregisteredFallbackBlockName.

You'll need to update your branch to bring in the latest master changes.

@rahulsprajapati
Copy link
Contributor Author

As of #8274, getUnknownTypeHandlerName has been replaced by two functions getFreeformFallbackBlockName and getUnregisteredFallbackBlockName.

sure, will update this.

@@ -360,6 +361,20 @@ export function isEditedPostSaveable( state ) {
);
}

/**
Copy link
Member

Choose a reason for hiding this comment

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

One of the test failures is due to the fact that documentation needs to be regenerated by npm run docs:build.

@@ -375,7 +390,7 @@ export function isEditedPostEmpty( state ) {
// operation by comparison. Since this function can be called frequently,
// optimize for the fast case where saveable blocks are non-empty.
return (
! getBlocksForSerialization( state ).length &&
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, in retrospect my original suggestion may have been made at a time before we'd considered the extra condition added by getBlocksForSerialization to consider the empty paragraph (see #9808). I've got to imagine this change here regresses on that intended behavior.

Maybe we should update hasKnownBlocks to accept blocks as an argument, so we can pass the result of getBlocksForSerialization to use in place of getBlockOrder.

export function hasKnownBlocks( state ) {
return getBlockOrder( state ).some( ( clientId ) => (
state.editor.present.blocksByClientId[ clientId ].name !== getFreeformContentHandlerName() &&
state.editor.present.blocksByClientId[ clientId ].name !== getUnregisteredTypeHandlerName()
Copy link
Member

Choose a reason for hiding this comment

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

The split of these two functions has me wondering if the semantics are meaningfully different enough to consider. "Unknown" would be truly a block, with demarcations, and thus non-empty for save. I think what we're really targeting is specifically freeform content.

Thus, I think we don't need this line and it could be dropped.

In doing so, and in reconsideration of targeting specifically freeform content, I wonder if the function name we've chosen hasKnownBlocks is very accurate. I'll need to sit and think on this one...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wonder if the function name we've chosen hasKnownBlocks is very accurate. I'll need to sit and think on this one...

If we are just going to check that the post have any freeform block or not then hasFreeFromBlock name will be more suitable for this.

Copy link
Member

Choose a reason for hiding this comment

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

That name seems to suit the purpose, but it highlights to me that it's so specific to the isEditedPostEmpty use-case that we might be better off to include the logic there, rather than to expose a new separate selector.

Thinking something like (haven't verified the logic):

export function isEditedPostEmpty( state ) {
	const blocks = getBlocksForSerialization( state );
	if ( ! blocks.length ) {
		return true;
	}

	const freeformBlockName = getFreeformContentHandlerName();
	if ( ! some( blocks, { name: freeformBlockName } ) ) {
		return false;
	}

	// Content serialization is considered an expensive operation, and should
	// only be considered after more trivial operations of assuming presence of
	// non-freeform blocks as implying that serializable content exists.
	return ! getEditedPostAttribute( state, 'content' );
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@aduth This works correctly.

Copy link

@sanketio sanketio Oct 31, 2018

Choose a reason for hiding this comment

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

@rahulsprajapati @aduth : Yup, the code works correctly, but while writing test cases for this, existing test case should return false if edits include a non-empty content property fails. So I think need to check for a more suitable solution. What do you guys say?

@youknowriad youknowriad modified the milestones: 4.2, WordPress 5.0 Oct 30, 2018
@aduth
Copy link
Member

aduth commented Oct 30, 2018

There appear to be some test failures for the impacted selectors. This is otherwise looking quite nice.

@rahulsprajapati
Copy link
Contributor Author

Great. I'll look into tests failure.

@aduth
Copy link
Member

aduth commented Oct 31, 2018

I looked over the tests and found that we had a few issues in their authoring:

  • We assumed core/freeform was the name of the freeform handler, but this is not assigned in the test environment (intentionally so).
    • The tests now register and set a freeform content handler test block
  • The block entries lacked a necessary isValid flag which is important for consideration by the block serializer

I also discovered a completely separate bug in our optimization of blocks existing, in that a content edit should always take precedence. This can occur if, for example, the post has blocks but the user switches to the Code Editor mode and removes all content. It's my understanding based on the current implementation in master that this would be an existing issue, solved in the most recent commit by considering the content edit.

I've further revised the implementation to avoid Array#some, since we can be certain that if there is more than one block, even if both blocks are the freeform block, and both freeform blocks produce an empty save result, that the overall serialized result would still be non-empty thanks to the Array#join which occurs in the serializer. This allows us to consider a trivial operation (and common occurrence) of blocks.length > 1 as non-empty.

@rahulsprajapati
Copy link
Contributor Author

Thank you @aduth for the explanation. 🙂

I was looking over the test only and was wondering why it's failing when the test case was correct and then just found that free-form was not detected with getFreeformContentHandlerName() but I guess got too slow 😅

@aduth aduth force-pushed the fix/empty-classic-block-update-fail branch 2 times, most recently from 280f8ae to a25bc1a Compare October 31, 2018 19:24
@aduth aduth force-pushed the fix/empty-classic-block-update-fail branch from a25bc1a to 4c07ae6 Compare October 31, 2018 20:21
@danielbachhuber danielbachhuber merged commit fae7777 into WordPress:master Oct 31, 2018
@danielbachhuber
Copy link
Member

I've manually verified that autosave doesn't fire when Title is empty and the post only has a Classic Block.

Thanks for your work on this @rahulsprajapati, @aduth, @sanketio

daniloercoli added a commit that referenced this pull request Nov 1, 2018
…rnmobile/port-quote-block-step-1

* 'master' of https://github.com/WordPress/gutenberg: (22 commits)
  Add removed periods to block descriptions. (#11367)
  Remove findDOMNode usage from the inserter (#11363)
  Remove deprecated componentWillReceiveProps from TinyMCE component (#11368)
  Create file blocks when dropping multiple files at once (#11297)
  Try avoiding the deprecated findDOMNode API from DropZone Provider (#11168)
  Fix: make meta+A behaviour of selecting all blocks work on safari and firefox. (#8180)
  Remove _wpGutenbergCodeEditorSettings and wp.codeEditor assets (#11342)
  Remove the Cloudflare warning (#11350)
  Image Block: Use source_url for media file link (#11254)
  Enhance styling of nextpage block using the Hr element (#11354)
  Embed block refactor and tidy (#10958)
  Nonce Middleware: Wrap the nonce middleware function into it's own function that isn't regenerated on every API request. (#11347)
  Fix RTL block alignments (#11293)
  RichText: fix buggy enter/delete behaviour (#11287)
  Remove code coverage setup (#11198)
  Parser: Runs all parser implementations against the same tests (#11320)
  Stop trying to autosave when title and classic block content both are empty. (#10404)
  Fix "Mac OS" typo + use fancy quotes consistently (#11310)
  Update documentation link paths (#11324)
  Editor: Reshape blocks state under own key (#11315)
  ...

# Conflicts:
#	gutenberg-mobile
@mtias mtias modified the milestones: WordPress 5.0 RC, 4.3 Nov 12, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Backwards Compatibility Issues or PRs that impact backwards compatability [Feature] Saving Related to saving functionality [Type] Bug An existing feature does not function as intended
Projects
None yet
Development

Successfully merging this pull request may close these issues.

400 bad request trying to autosave empty content
6 participants