55 * the sync pulls the cloud's old order and applies it locally, reverting the
66 * user's changes. The cloud's stale indices overwrite local modifications.
77 *
8- * Fix: Skip cloud-to-local index updates and reordering for bookmarks that are
9- * in the locallyModifiedBookmarkIds set. The local order takes priority and
10- * gets pushed to cloud instead.
8+ * Fix: Skip ALL cloud-to-local updates (title, folder, index) for bookmarks
9+ * that are in the locallyModifiedBookmarkIds set. The local state takes
10+ * priority and gets pushed to cloud instead.
1111 *
1212 * Three fix points tested:
13- * 1. categorizeCloudBookmarks() - skips index-only updates for locally modified bookmarks
14- * 2. updateLocalBookmarksFromCloud() - skips index-only moves for locally modified bookmarks
13+ * 1. categorizeCloudBookmarks() - skips ALL cloud updates for locally modified bookmarks
14+ * 2. updateLocalBookmarksFromCloud() - skips ALL cloud updates for locally modified bookmarks
1515 * 3. reorderLocalToMatchCloud() - skips reorder for folders with locally modified children
1616 */
1717
@@ -46,7 +46,8 @@ function bookmarkNeedsUpdate(cloudBm, localBm) {
4646}
4747
4848/**
49- * categorizeCloudBookmarks WITH the fix: skip index-only updates for locally modified bookmarks
49+ * categorizeCloudBookmarks WITH the fix: skip ALL cloud updates for locally modified bookmarks.
50+ * Local state takes priority and will be pushed to cloud in the push phase.
5051 */
5152function categorizeCloudBookmarks (
5253 cloudBookmarks ,
@@ -80,17 +81,11 @@ function categorizeCloudBookmarks(
8081 if ( ! localBm ) {
8182 toAdd . push ( cloudBm ) ;
8283 } else if ( bookmarkNeedsUpdate ( cloudBm , localBm ) ) {
83- // FIX: Skip index-only updates for locally modified bookmarks
84+ // FIX: Skip ALL cloud updates for locally modified bookmarks.
85+ // Local wins for title, folder, AND index changes.
8486 if ( locallyModifiedBookmarkIds . has ( localBm . id ) ) {
85- const cloudFolder = normalizeFolderPath ( cloudBm . folderPath ) ;
86- const localFolder = normalizeFolderPath ( localBm . folderPath ) ;
87- const titleChanged = ( cloudBm . title ?? '' ) !== ( localBm . title ?? '' ) ;
88- const folderChanged = cloudFolder !== localFolder ;
89-
90- if ( ! titleChanged && ! folderChanged ) {
91- skippedByLocalModification . push ( cloudBm . url ) ;
92- continue ;
93- }
87+ skippedByLocalModification . push ( cloudBm . url ) ;
88+ continue ;
9489 }
9590 toUpdate . push ( { cloud : cloudBm , local : localBm } ) ;
9691 } else {
@@ -178,7 +173,7 @@ describe('Bug Fix: Bookmark reorder should NOT reset on Sync Now', () => {
178173 expect ( result . skippedByLocalModification . length ) . toBe ( 5 ) ;
179174 } ) ;
180175
181- it ( 'should still allow title changes from cloud even for locally modified bookmarks' , ( ) => {
176+ it ( 'should skip cloud title changes for locally modified bookmarks (local wins) ' , ( ) => {
182177 const cloudBookmarks = [
183178 {
184179 url : 'https://github.com' ,
@@ -207,12 +202,13 @@ describe('Bug Fix: Bookmark reorder should NOT reset on Sync Now', () => {
207202 locallyModifiedBookmarkIds
208203 ) ;
209204
210- // Title changed in cloud - should still be updated
211- expect ( result . toUpdate . length ) . toBe ( 1 ) ;
212- expect ( result . skippedByLocalModification . length ) . toBe ( 0 ) ;
205+ // Locally modified bookmark — local wins, cloud title change is skipped.
206+ // The local state will be pushed to cloud in the push phase.
207+ expect ( result . toUpdate . length ) . toBe ( 0 ) ;
208+ expect ( result . skippedByLocalModification . length ) . toBe ( 1 ) ;
213209 } ) ;
214210
215- it ( 'should still allow folder changes from cloud even for locally modified bookmarks' , ( ) => {
211+ it ( 'should skip cloud folder changes for locally modified bookmarks (local wins) ' , ( ) => {
216212 const cloudBookmarks = [
217213 { url : 'https://github.com' , title : 'GitHub' , folderPath : 'Bookmarks Bar/Work' , index : 0 } ,
218214 ] ;
@@ -236,9 +232,10 @@ describe('Bug Fix: Bookmark reorder should NOT reset on Sync Now', () => {
236232 locallyModifiedBookmarkIds
237233 ) ;
238234
239- // Folder changed in cloud - should still be updated
240- expect ( result . toUpdate . length ) . toBe ( 1 ) ;
241- expect ( result . skippedByLocalModification . length ) . toBe ( 0 ) ;
235+ // Locally modified bookmark — local wins, cloud folder change is skipped.
236+ // The local state will be pushed to cloud in the push phase.
237+ expect ( result . toUpdate . length ) . toBe ( 0 ) ;
238+ expect ( result . skippedByLocalModification . length ) . toBe ( 1 ) ;
242239 } ) ;
243240
244241 it ( 'should update bookmarks normally when they are NOT locally modified' , ( ) => {
@@ -303,17 +300,17 @@ describe('Bug Fix: Bookmark reorder should NOT reset on Sync Now', () => {
303300 locallyModifiedBookmarkIds
304301 ) ;
305302
306- // bm-1: locally modified, index-only change - > skip
307- // bm-2: locally modified, index-only change - > skip
308- // bm-3: NOT locally modified, title changed -> update
303+ // bm-1: locally modified - > skip (local wins)
304+ // bm-2: locally modified - > skip (local wins)
305+ // bm-3: NOT locally modified, title changed -> update from cloud
309306 expect ( result . skippedByLocalModification . length ) . toBe ( 2 ) ;
310307 expect ( result . toUpdate . length ) . toBe ( 1 ) ;
311308 expect ( result . toUpdate [ 0 ] . cloud . url ) . toBe ( 'https://c.com' ) ;
312309 } ) ;
313310 } ) ;
314311
315312 describe ( 'updateLocalBookmarksFromCloud - skip index-only moves for locally modified' , ( ) => {
316- it ( 'should skip index-only moves for locally modified bookmarks' , ( ) => {
313+ it ( 'should skip ALL cloud updates for locally modified bookmarks' , ( ) => {
317314 const locallyModifiedBookmarkIds = new Set ( [ 'bm-1' , 'bm-2' , 'bm-3' ] ) ;
318315
319316 const bookmarksToUpdate = [
@@ -344,32 +341,21 @@ describe('Bug Fix: Bookmark reorder should NOT reset on Sync Now', () => {
344341 const updated = [ ] ;
345342
346343 for ( const { cloud, local } of bookmarksToUpdate ) {
347- const titleChanged = ( cloud . title ?? '' ) !== ( local . title ?? '' ) ;
348- const folderChanged =
349- normalizeFolderPath ( cloud . folderPath ) !== normalizeFolderPath ( local . folderPath ) ;
350- const indexChanged =
351- cloud . index !== undefined && local . index !== undefined && cloud . index !== local . index ;
352-
353- // FIX: skip index-only moves for locally modified bookmarks
354- if (
355- locallyModifiedBookmarkIds . has ( local . id ) &&
356- ! titleChanged &&
357- ! folderChanged &&
358- indexChanged
359- ) {
344+ // FIX: skip ALL cloud updates for locally modified bookmarks
345+ if ( locallyModifiedBookmarkIds . has ( local . id ) ) {
360346 skipped . push ( cloud . url ) ;
361347 continue ;
362348 }
363349
364350 updated . push ( cloud . url ) ;
365351 }
366352
367- // Both bookmarks have index-only changes and are locally modified
353+ // Both bookmarks are locally modified — local wins
368354 expect ( skipped ) . toEqual ( [ 'https://a.com' , 'https://b.com' ] ) ;
369355 expect ( updated ) . toEqual ( [ ] ) ;
370356 } ) ;
371357
372- it ( 'should still update title even for locally modified bookmarks' , ( ) => {
358+ it ( 'should skip ALL cloud updates for locally modified bookmarks (including title) ' , ( ) => {
373359 const locallyModifiedBookmarkIds = new Set ( [ 'bm-1' ] ) ;
374360
375361 const bookmarksToUpdate = [
@@ -394,28 +380,18 @@ describe('Bug Fix: Bookmark reorder should NOT reset on Sync Now', () => {
394380 const updated = [ ] ;
395381
396382 for ( const { cloud, local } of bookmarksToUpdate ) {
397- const titleChanged = ( cloud . title ?? '' ) !== ( local . title ?? '' ) ;
398- const folderChanged =
399- normalizeFolderPath ( cloud . folderPath ) !== normalizeFolderPath ( local . folderPath ) ;
400- const indexChanged =
401- cloud . index !== undefined && local . index !== undefined && cloud . index !== local . index ;
402-
403- if (
404- locallyModifiedBookmarkIds . has ( local . id ) &&
405- ! titleChanged &&
406- ! folderChanged &&
407- indexChanged
408- ) {
383+ // FIX: skip ALL cloud updates for locally modified bookmarks
384+ if ( locallyModifiedBookmarkIds . has ( local . id ) ) {
409385 skipped . push ( cloud . url ) ;
410386 continue ;
411387 }
412388
413389 updated . push ( cloud . url ) ;
414390 }
415391
416- // Title changed - should NOT be skipped
417- expect ( skipped ) . toEqual ( [ ] ) ;
418- expect ( updated ) . toEqual ( [ 'https://a.com' ] ) ;
392+ // Locally modified — local wins, cloud title change is skipped
393+ expect ( skipped ) . toEqual ( [ 'https://a.com' ] ) ;
394+ expect ( updated ) . toEqual ( [ ] ) ;
419395 } ) ;
420396 } ) ;
421397
@@ -697,8 +673,8 @@ describe('Bug Fix: Bookmark reorder should NOT reset on Sync Now', () => {
697673 expect ( result . skippedByLocalModification . length ) . toBe ( 1 ) ;
698674 } ) ;
699675
700- it ( 'should detect actual folder changes even after normalization' , ( ) => {
701- // Cloud moved bookmark to a subfolder - this is a real change
676+ it ( 'should skip cloud folder changes for locally modified bookmarks even after normalization' , ( ) => {
677+ // Cloud moved bookmark to a subfolder - but bookmark is locally modified, so local wins
702678 const cloudBookmarks = [
703679 { url : 'https://a.com' , title : 'A' , folderPath : 'Bookmarks Bar/Work' , index : 0 } ,
704680 ] ;
@@ -716,9 +692,9 @@ describe('Bug Fix: Bookmark reorder should NOT reset on Sync Now', () => {
716692 locallyModifiedBookmarkIds
717693 ) ;
718694
719- // Folder changed (toolbar/ vs toolbar/Work) - should be updated
720- expect ( result . toUpdate . length ) . toBe ( 1 ) ;
721- expect ( result . skippedByLocalModification . length ) . toBe ( 0 ) ;
695+ // Locally modified — local wins, cloud folder change is skipped
696+ expect ( result . toUpdate . length ) . toBe ( 0 ) ;
697+ expect ( result . skippedByLocalModification . length ) . toBe ( 1 ) ;
722698 } ) ;
723699 } ) ;
724700} ) ;
0 commit comments