@@ -21,7 +21,11 @@ import {
2121} from './crdt-blocks' ;
2222import { type Post } from '../entity-types/post' ;
2323import { type Type } from '../entity-types' ;
24- import { CRDT_DOC_META_PERSISTENCE_KEY , CRDT_RECORD_MAP_KEY } from '../sync' ;
24+ import {
25+ CRDT_DOC_META_PERSISTENCE_KEY ,
26+ CRDT_RECORD_MAP_KEY ,
27+ WORDPRESS_META_KEY_FOR_CRDT_DOC_PERSISTENCE ,
28+ } from '../sync' ;
2529import type { WPBlockSelection , WPSelection } from '../types' ;
2630
2731export type PostChanges = Partial < Post > & {
@@ -44,6 +48,7 @@ const allowedPostProperties = new Set< string >( [
4448 'featured_media' ,
4549 'format' ,
4650 'ping_status' ,
51+ 'meta' ,
4752 'slug' ,
4853 'status' ,
4954 'sticky' ,
@@ -52,6 +57,11 @@ const allowedPostProperties = new Set< string >( [
5257 'title' ,
5358] ) ;
5459
60+ // Post meta keys that should *not* be synced.
61+ const disallowedPostMetaKeys = new Set < string > ( [
62+ WORDPRESS_META_KEY_FOR_CRDT_DOC_PERSISTENCE ,
63+ ] ) ;
64+
5565/**
5666 * Given a set of local changes to a generic entity record, apply those changes
5767 * to the local Y.Doc.
@@ -94,13 +104,13 @@ export function defaultApplyChangesToCRDTDoc(
94104 *
95105 * @param {CRDTDoc } ydoc
96106 * @param {PostChanges } changes
97- * @param {Type } postType
107+ * @param {Type } _postType
98108 * @return {void }
99109 */
100110export function applyPostChangesToCRDTDoc (
101111 ydoc : CRDTDoc ,
102112 changes : PostChanges ,
103- postType : Type // eslint-disable-line @typescript-eslint/no-unused-vars
113+ _postType : Type // eslint-disable-line @typescript-eslint/no-unused-vars
104114) : void {
105115 const ymap = ydoc . getMap ( CRDT_RECORD_MAP_KEY ) ;
106116
@@ -148,6 +158,36 @@ export function applyPostChangesToCRDTDoc(
148158 break ;
149159 }
150160
161+ // "Meta" is overloaded term; here, it refers to post meta.
162+ case 'meta' : {
163+ let metaMap = ymap . get ( 'meta' ) as Y . Map < unknown > ;
164+
165+ // Initialize.
166+ if ( ! ( metaMap instanceof Y . Map ) ) {
167+ metaMap = new Y . Map ( ) ;
168+ setValue ( metaMap ) ;
169+ }
170+
171+ // Iterate over each meta property in the new value and merge it if it
172+ // should be synced.
173+ Object . entries ( newValue ?? { } ) . forEach (
174+ ( [ metaKey , metaValue ] ) => {
175+ if ( disallowedPostMetaKeys . has ( metaKey ) ) {
176+ return ;
177+ }
178+
179+ mergeValue (
180+ metaMap . get ( metaKey ) , // current value in CRDT
181+ metaValue , // new value from changes
182+ ( updatedMetaValue : unknown ) : void => {
183+ metaMap . set ( metaKey , updatedMetaValue ) ;
184+ }
185+ ) ;
186+ }
187+ ) ;
188+ break ;
189+ }
190+
151191 case 'slug' : {
152192 // Do not sync an empty slug. This indicates that the post is using
153193 // the default auto-generated slug.
@@ -200,17 +240,19 @@ export function defaultGetChangesFromCRDTDoc( crdtDoc: CRDTDoc ): ObjectData {
200240 *
201241 * @param {CRDTDoc } ydoc
202242 * @param {Post } editedRecord
203- * @param {Type } postType
243+ * @param {Type } _postType
204244 * @return {Partial<PostChanges> } The changes that should be applied to the local record.
205245 */
206246export function getPostChangesFromCRDTDoc (
207247 ydoc : CRDTDoc ,
208248 editedRecord : Post ,
209- postType : Type // eslint-disable-line @typescript-eslint/no-unused-vars
249+ _postType : Type // eslint-disable-line @typescript-eslint/no-unused-vars
210250) : PostChanges {
211251 const ymap = ydoc . getMap ( CRDT_RECORD_MAP_KEY ) ;
212252
213- return Object . fromEntries (
253+ let allowedMetaChanges : Post [ 'meta' ] = { } ;
254+
255+ const changes = Object . fromEntries (
214256 Object . entries ( ymap . toJSON ( ) ) . filter ( ( [ key , newValue ] ) => {
215257 if ( ! allowedPostProperties . has ( key ) ) {
216258 return false ;
@@ -271,6 +313,24 @@ export function getPostChangesFromCRDTDoc(
271313 return haveValuesChanged ( currentValue , newValue ) ;
272314 }
273315
316+ case 'meta' : {
317+ allowedMetaChanges = Object . fromEntries (
318+ Object . entries ( newValue ?? { } ) . filter (
319+ ( [ metaKey ] ) =>
320+ ! disallowedPostMetaKeys . has ( metaKey )
321+ )
322+ ) ;
323+
324+ // Merge the allowed meta changes with the current meta values since
325+ // not all meta properties are synced.
326+ const mergedValue = {
327+ ...( currentValue as PostChanges [ 'meta' ] ) ,
328+ ...allowedMetaChanges ,
329+ } ;
330+
331+ return haveValuesChanged ( currentValue , mergedValue ) ;
332+ }
333+
274334 case 'status' : {
275335 // Do not sync an invalid status.
276336 if ( 'auto-draft' === newValue ) {
@@ -296,6 +356,17 @@ export function getPostChangesFromCRDTDoc(
296356 }
297357 } )
298358 ) ;
359+
360+ // Meta changes must be merged with the edited record since not all meta
361+ // properties are synced.
362+ if ( 'object' === typeof changes . meta ) {
363+ changes . meta = {
364+ ...editedRecord . meta ,
365+ ...allowedMetaChanges ,
366+ } ;
367+ }
368+
369+ return changes ;
299370}
300371
301372/**
0 commit comments