Post is a bag of attributes like title
, content
, metadata
or terms
.
Some are primitive values, others are complex objects.
The copy of the post saved on server is part of the common post state tree at
state.posts.queries
. In the UI code known as currentPost
or savedPost
.
Local edits are stored in state.posts.edits
. Modified attributes only, diff from the saved post.
getEditedPost( state, siteId, postId )
selector merges them together and returns the post as the editor displays it. In the UI code known as post
.
When saving, the server returns the full post object as it knows it.
Locally, we update the savedPost
and merge the changes from server into
the local edits
. On most saves, that makes edits
empty and the post
no longer dirty.
Set up the post editor to edit a post. New post is initialized with default attribute values and doesn't have an ID until its first save.
Editing existing post usually needs to wait until the post is fetched from the server.
Existing post arrived from the server, initialize the editor with its attributes
Save the modifications to server. Sends the modified attributes.
Save requests return the full copy of the post as the server knows it. This actions
updates the local savedPost
and updates the edits
.
There are two types of autosave. Autosaving drafts is just a synonym to a regular save. Autosaving published posts doesn't update the publicly visible post, but saves into a "hidden" location. Jetpack/WP.com feature, not available in core.
Set everything to null
Merging edits sometimes requires some sophistication:
In a post from server, featured_image
is either null
, or it's an URL. The image's ID is in post_thumbnail.ID
. When sending a modify request, featured_image
attribute in edits
contains the ID.
When publishing or scheduling a post, we send status
of publish
and a date
property. The server can however return publish
, future
or pending
. We need to accept a different status like this as a performed edit, otherwise the post remains dirty.
Similar troubles are with categories etc.
Array of key, value pairs:
[
{ key: 'geo_public', value: '1' }
]
To modify, we send "update" and "delete" operations as edits:
[
{ key: 'geo_public', operation: 'delete' }
]
We can choose to edit a new post as a duplicate of another. Needs care about what attributes we copy. If we copied everything, then publicize would break, for example.
We (auto)save the post before showing a preview. Subtle async logic that's easy to break.
Sometimes the post is in such a state that it cannot be saved. For example, while an image is being uploaded. Then the post body contains a shortcode with a temporary ID or featured image has a temporary ID.
In such case, actions to block and unblock save are dispatched and save action looks at the block status.
Redux state has a content
attribute. TinyMCE contains some content. We don't sync them on every keystroke for performance reasons. editor.getContent()
is slow and does some postprocessing on the HTML markup. We only update the Redux content
immediately before save or when switching editor mode.
To show correct isDirty
status, we maintain a rawContent
property that uses a more efficent editor.getContent()
implementation and is debounced/throttled.
We sync the content the other way (from Redux to TinyMCE) in a few cases:
- new post started being edited (empty)
- post to edit loaded
- started to edit a post copy
- revision from history was loaded
Tricky and hard to understand in React: detect the exact event in lifecycle method
The REST endpoint to retrieve a post has a context
param that can be edit
, display
etc. There is some title
, content
and excerpt
processing for the edit
context.
Now, in state.posts.queries
, we have mixed post data from display
and edit
contexts. Causes some content corruption: escaping entities, newlines etc.