Skip to content

Commit 38436fc

Browse files
committed
fix: simplify sync ordering — cloud always wins on pull, remove local-wins logic
Key changes: - Remove locallyModifiedBookmarkIds skip in categorizeCloudBookmarks: Cloud order always takes precedence on pull. Since sync happens immediately, there's no real conflict scenario between browsers. - Simplify hasLocalChangesToPush to just checksum comparison: Instead of tracking multiple conditions (local additions, modified IDs, tombstone counts), simply push if local state differs from cloud. - Fix onRemoved handler: Don't pollute locallyModifiedBookmarkIds during sync-driven operations (respect isSyncDrivenChange flag like other handlers). Root cause: The 'local wins' logic was preventing cloud ordering updates from being applied when the pulling browser had any previously modified bookmarks. This caused bookmark order to not be preserved after sync.
1 parent d52ad87 commit 38436fc

1 file changed

Lines changed: 17 additions & 30 deletions

File tree

apps/extension/src/background/index.js

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -454,17 +454,19 @@ function bookmarkNeedsUpdate(cloudBm, localBm) {
454454
* @param {Array} tombstones - Merged tombstones
455455
* @returns {{toAdd: Array, toUpdate: Array, skippedByTombstone: Array}} - Categorized bookmarks
456456
*/
457-
function categorizeCloudBookmarks(cloudBookmarks, localBookmarks, tombstones, modifiedLocalIds) {
457+
function categorizeCloudBookmarks(cloudBookmarks, localBookmarks, tombstones) {
458458
const localByUrl = new Map(localBookmarks.filter((b) => b.url).map((b) => [b.url, b]));
459459

460460
const toAdd = [];
461461
const toUpdate = [];
462462
const skippedByTombstone = [];
463-
const skippedByLocalModification = [];
463+
// NOTE: locallyModifiedBookmarkIds is intentionally NOT used here.
464+
// Per design: cloud always wins on pull. The pushing browser's order is truth.
465+
// Since sync happens immediately, there's no real conflict scenario.
464466
const alreadyExistsUnchanged = [];
465467

466468
console.log(
467-
`[MarkSyncr] categorizeCloudBookmarks: ${cloudBookmarks.length} cloud, ${localBookmarks.length} local, ${tombstones.length} tombstones, ${modifiedLocalIds?.size || 0} locally modified`
469+
`[MarkSyncr] categorizeCloudBookmarks: ${cloudBookmarks.length} cloud, ${localBookmarks.length} local, ${tombstones.length} tombstones`
468470
);
469471

470472
for (const cloudBm of cloudBookmarks) {
@@ -491,9 +493,6 @@ function categorizeCloudBookmarks(cloudBookmarks, localBookmarks, tombstones, mo
491493
const localBm = localByUrl.get(cloudBm.url);
492494
if (!localBm) {
493495
toAdd.push(cloudBm);
494-
} else if (modifiedLocalIds?.has(localBm.id)) {
495-
// Local bookmark was modified by the user since last sync - local wins
496-
skippedByLocalModification.push(cloudBm.url);
497496
} else if (bookmarkNeedsUpdate(cloudBm, localBm)) {
498497
toUpdate.push({ cloud: cloudBm, local: localBm });
499498
} else {
@@ -511,23 +510,14 @@ function categorizeCloudBookmarks(cloudBookmarks, localBookmarks, tombstones, mo
511510
}
512511
}
513512

514-
if (skippedByLocalModification.length > 0) {
515-
console.log(
516-
`[MarkSyncr] 🏠 Skipped ${skippedByLocalModification.length} cloud→local updates (locally modified, local wins):`
517-
);
518-
for (const url of skippedByLocalModification.slice(0, 5)) {
519-
console.log(` - ${url}`);
520-
}
521-
}
522-
523513
if (alreadyExistsUnchanged.length > 0) {
524514
console.log(
525515
`[MarkSyncr] ✓ ${alreadyExistsUnchanged.length} cloud bookmarks already exist locally (unchanged)`
526516
);
527517
}
528518

529519
console.log(
530-
`[MarkSyncr] Categorization result: toAdd=${toAdd.length}, toUpdate=${toUpdate.length}, skipped=${skippedByTombstone.length}, localWins=${skippedByLocalModification.length}, unchanged=${alreadyExistsUnchanged.length}`
520+
`[MarkSyncr] Categorization result: toAdd=${toAdd.length}, toUpdate=${toUpdate.length}, skipped=${skippedByTombstone.length}, unchanged=${alreadyExistsUnchanged.length}`
531521
);
532522

533523
return { toAdd, toUpdate, skippedByTombstone };
@@ -1282,9 +1272,11 @@ function setupBookmarkListeners() {
12821272
return;
12831273
}
12841274

1285-
// Always track locally modified bookmarks, even during sync
1286-
locallyModifiedBookmarkIds.add(id);
1287-
debouncedSaveLocallyModifiedIds();
1275+
// Only track user-initiated removals, not sync-driven ones
1276+
if (!isSyncDrivenChange) {
1277+
locallyModifiedBookmarkIds.add(id);
1278+
debouncedSaveLocallyModifiedIds();
1279+
}
12881280

12891281
// ALWAYS create tombstones for deleted bookmarks, even during sync.
12901282
// Without a tombstone, the next sync will re-add the bookmark from cloud.
@@ -1644,8 +1636,7 @@ async function performSync(sourceId) {
16441636
const { toAdd: newFromCloud, toUpdate: bookmarksToUpdate } = categorizeCloudBookmarks(
16451637
cloudBookmarks,
16461638
updatedLocalFlat,
1647-
mergedTombstones,
1648-
locallyModifiedBookmarkIds
1639+
mergedTombstones
16491640
);
16501641

16511642
console.log(`[MarkSyncr] 🔍 New bookmarks from cloud: ${newFromCloud.length}`);
@@ -1741,15 +1732,11 @@ async function performSync(sourceId) {
17411732
console.log(`[MarkSyncr] Local checksum: ${localChecksum}`);
17421733
console.log(`[MarkSyncr] Cloud checksum: ${cloudChecksum}`);
17431734

1744-
// Determine if we have LOCAL (user-initiated) changes to push to cloud
1745-
// NOTE: bookmarksToUpdate are cloud→local updates (received FROM cloud), NOT local changes.
1746-
// Including them here was causing the receiving browser to re-push cloud data back,
1747-
// overwriting the original browser's ordering changes.
1748-
const hasLocalChangesToPush =
1749-
localAdditions.length > 0 ||
1750-
locallyModifiedBookmarkIds.size > 0 ||
1751-
localTombstones.length > cloudTombstones.length ||
1752-
deletedLocally > 0;
1735+
// Simplified push logic: push if local state differs from cloud.
1736+
// The pushing browser's order is truth. After pull+reorder, if our local
1737+
// state differs from cloud (new local bookmarks, tombstone deletions, etc.),
1738+
// push the current state. Checksum comparison handles this cleanly.
1739+
const hasLocalChangesToPush = localChecksum !== cloudChecksum;
17531740

17541741
// Check if there were any changes during this sync
17551742
const hasChanges =

0 commit comments

Comments
 (0)