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

fix: ensure draft changes #3306

Merged
merged 5 commits into from
Feb 28, 2020
Merged

fix: ensure draft changes #3306

merged 5 commits into from
Feb 28, 2020

Conversation

barthc
Copy link
Contributor

@barthc barthc commented Feb 23, 2020

Closes #3256

Now that we are using the diff API to pull workflow changes, it makes sense to make sure that they are actual draft changes before saving by comparing the current entry draft with unpublished or published Entry.

The changes also take care of the relation widget unsaved changes bug #725.

@barthc barthc requested a review from a team February 23, 2020 14:32
@erezrokah erezrokah self-requested a review February 24, 2020 11:13
@erezrokah
Copy link
Contributor

I like the approach in this PR, will try to finalize the review soon.

@erezrokah
Copy link
Contributor

Realized this is similar to #3305, will go over both of them

@erezrokah
Copy link
Contributor

@barthc
Copy link
Contributor Author

barthc commented Feb 26, 2020

I think it's the markdown widget, the rich text mode is inserting a tab at the beginning of the string on edit. But the markdown mode is good. Is it a bug ?

@erezrokah
Copy link
Contributor

erezrokah commented Feb 26, 2020

Can you share a way to get that tab inserted? I couldn't reproduce it.

I was able to fix my issue by removing the extra line break inserted by gray-matter.
See here 411d986.

This might have some non-intuitive results as our markdown widget seems to strip line breaks at the end. Notice in the following GIF, it won't report a change until a non line break character is inserted:
gray_matter_line_breaks

Looking into that now

@barthc
Copy link
Contributor Author

barthc commented Feb 26, 2020

Here is the markdown mode:

Peek 2019-11-24 23-57

@erezrokah
Copy link
Contributor

erezrokah commented Feb 26, 2020

Markdown mode doesn't process anything so I think most of the issues won't come from there.
The rich text editor parses the raw markdown into an abstract tree and then reports it back as raw markdown. In that process it does some schema validations and other transformations that caused a few 'Unsaved Changes' issues in the past.

Comment on lines 94 to 105
let newState = state.withMutations(state => {
state.setIn(['entry', 'data', action.payload.field], action.payload.value);
state.mergeDeepIn(['fieldsMetaData'], fromJS(action.payload.metadata));
state.set('hasChanged', true);
});
const newStateData = newState.getIn(['entry', 'data']);

(newStateData.equals(unpublishedEntry.get('data')) ||
newStateData.equals(publishedEntry.get('data'))) &&
(newState = newState.set('hasChanged', false));

return newState;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be done with only a single Immutable node?

return state.withMutations(state => {
  state.setIn(['entry', 'data', action.payload.field], action.payload.value);
  state.mergeDeepIn(['fieldsMetaData'], fromJS(action.payload.metadata));

  const newData = state.getIn(['entry', 'data']);
  const matchesLastSaved = newData.equals(unpublishedEntry.get('data')) || newData.equals(publishedEntry.get('data'))
 
  state.set('hasChanged', !matchesLastSaved);
});

Copy link
Contributor

Choose a reason for hiding this comment

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

@bharrisau
Copy link
Contributor

Couple of comments/questions

  • Is .equals going to give the expected equality when matching maps? Immutable provides .is that gives good deep matching, but I'm only a flythrough contributor so I'm not clear enough on the codebase to know if .equals will do the same.
  • Is it worth adding a test spec for this behavior?
  • Is it possible to consolidate unpublishedEntry and publishedEntry into just the one lastSavedEntry or something? I'm not clear on why there are two possible states that can set hasChanged to false. As a user, there would only be one last saved version of the document right? so only one state that would remove the save button?

@bharrisau
Copy link
Contributor

I prefer this approach over the simple mod I proposed in #3305 - this looks like if you change something and then change it back it would recognize that the data matches the previous version.

@barthc
Copy link
Contributor Author

barthc commented Feb 26, 2020

Is it possible to consolidate unpublishedEntry and publishedEntry into just the one lastSavedEntry or something? I'm not clear on why there are two possible states that can set hasChanged to false. As a user, there would only be one last saved version of the document right? so only one state that would remove the save button?

It has to compare both entries, for example, if a user makes workflow changes and on subquest edits revert the workflow changes back to the published entry, we will still have the diff issue,

@bharrisau
Copy link
Contributor

Is it possible to consolidate unpublishedEntry and publishedEntry into just the one lastSavedEntry or something? I'm not clear on why there are two possible states that can set hasChanged to false. As a user, there would only be one last saved version of the document right? so only one state that would remove the save button?

It has to compare both entries, for example, if a user makes workflow changes and on subquest edits revert the workflow changes back to the published entry, we will still have the diff issue,

Might be a silly question (again, not familiar with the codebase) - but can picking between those two be the responsibility of the Editor.js when it is calling changeDraftField?

Alternatively, can you just pass through an array of entries that should be compared against? [unpublishedEntry, publishedEntry].filter(remove nulls)
Then the reducer API wouldn't need to be changed in the future if there were more states to compare against?

Probably bikeshedding now - but I see APIs where multiple fields are treated identically and I start asking if there needs to be multiple fields.

@erezrokah
Copy link
Contributor

erezrokah commented Feb 26, 2020

our markdown widget seems to strip line breaks

This happens here. Tried removing it, but created other issues when parsing loading the saved markdown.

@barthc
Copy link
Contributor Author

barthc commented Feb 26, 2020

Alternatively, can you just pass through an array of entries that should be compared against? [unpublishedEntry, publishedEntry].filter(remove nulls)
Then the reducer API wouldn't need to be changed in the future if there were more states to compare against?

So the possible values for unpublishedEntry, publishedEntry at any time could be empty Map() for both, one empty and the other with some value, and then both have values.

I see your point, yes we can do that.

@barthc
Copy link
Contributor Author

barthc commented Feb 26, 2020

Is .equals going to give the expected equality when matching maps? Immutable provides .is that gives good deep matching, but I'm only a flythrough contributor so I'm not clear enough on the codebase to know if .equals will do the same.

Looks like equals is calling is under the hood https://immutable-js.github.io/immutable-js/docs/#/Collection/equals

@bharrisau
Copy link
Contributor

Looks like equals is calling is under the hood https://immutable-js.github.io/immutable-js/docs/#/Collection/equals

No worries. And don't feel like you need to change anything above (unpublished/published) - I just make comments about possible alternatives in reviews because that's how I like others to review my stuff.

@barthc
Copy link
Contributor Author

barthc commented Feb 26, 2020

@erezrokah any luck with the markdown ?

@erezrokah
Copy link
Contributor

erezrokah commented Feb 26, 2020

Not yet and we might want to handle some of those issues in another PR.
We should at least make sure gray-matter doesn't add that line break as at the moment it seems we will always add it on save, but never report it in the value of the change event.
I'll keep looking

@@ -72,7 +72,7 @@ class FrontmatterFormatter {
const result = matter(content, { engines: parsers, ...format });
return {
...result.data,
body: result.content,
...(result.content.trim() && { body: result.content }),
Copy link
Contributor

Choose a reason for hiding this comment

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

@barthc do you mind sharing the reason this is required?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's the same line break issue at the end of the file. So when the file is parsed, it includes a body key with line break as value, this would occur for config fields without the top-level markdown body widget that resolves to the body of the markdown.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks I think I get it now.
We pass in an empty body here:
https://github.com/netlify/netlify-cms/blob/3e873f3e0277bca8f5dee619e931a66d47779c3d/packages/netlify-cms-core/src/formats/frontmatter.js#L80

when it doesn't exist, so we have to remove that property on parse.

@erezrokah
Copy link
Contributor

@barthc are you good with me adding the other line break fix (411d986#diff-ac1a295de180688364c35e08b03a343fR89)?

We can fix the behaviour in #3306 (comment) in another PR.
#3256 and #725 seem more important to have fixed.

WDYT?

@barthc
Copy link
Contributor Author

barthc commented Feb 27, 2020

Yes please do, thanks.

@erezrokah
Copy link
Contributor

looking into the tests failures

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

CMS creates an empty branch and PR
3 participants