// Synchronization Utilities // Handles sync logic and conflict resolution (function() { 'use strict'; const SyncUtils = { // Sync mode descriptions MODES: { 'bi-directional': { name: 'Bi-Directional', description: 'Keep both versions; additions/updates replicate both ways', deleteFromLinkding: true, keepBothOnUpdate: true }, 'write-only': { name: 'Write-Only', description: 'Browser is authoritative; updates push to Linkding only', deleteFromLinkding: true, keepBothOnUpdate: false }, 'read-only': { name: 'Read-Only', description: 'Linkding is authoritative; download from Linkding only', deleteFromLinkding: false, keepBothOnUpdate: false } }, // Get sync mode config getModeConfig(mode) { return this.MODES[mode] || this.MODES['bi-directional']; }, // Determine sync action based on mode determineSyncAction(mode, action) { const config = this.getModeConfig(mode); switch (action) { case 'create': // New bookmark in browser if (config.deleteFromLinkding) { return { action: 'create', deleteFromLinkding: false }; } return { action: 'create' }; case 'update': // Existing bookmark with changes if (config.keepBothOnUpdate) { return { action: 'update', keepBoth: true }; } return { action: 'update' }; case 'delete': // Bookmark deleted from browser if (config.deleteFromLinkding) { return { action: 'delete' }; } return { action: 'ignore' }; default: return { action: 'none' }; } }, // Resolve conflict when same URL exists in different folders resolveConflict(browserBookmark, linkdingBookmark, browserNotes, linkdingNotes, mode) { const config = this.getModeConfig(mode); if (config.keepBothOnUpdate) { // Bi-directional: keep both, use Linkding notes as primary const mergedNotes = { path: browserNotes.path || linkdingNotes.path, userNotes: config.keepBothOnUpdate ? linkdingNotes.userNotes : browserNotes.userNotes, autoTags: [] }; return { action: 'update', notes: mergedNotes, note: 'Updated with browser path; Linkding notes preserved' }; } // Write-only or read-only: use browser data return { action: 'update', notes: browserNotes, note: 'Updated from browser' }; }, // Check if bookmark needs sync needsSync(browserBookmark, linkdingBookmark, browserNotes, linkdingNotes) { // Check if URLs match if (browserBookmark.url !== linkdingBookmark.url) { return true; } // Check if path changed if (browserNotes.path !== linkdingNotes.path) { return true; } // Check if notes changed if (browserNotes.userNotes !== linkdingNotes.userNotes) { return true; } // Check if tags changed const browserTags = browserNotes.autoTags.map(t => t.name); const linkdingTags = linkdingNotes.autoTags.map(t => t.name); if (JSON.stringify(browserTags) !== JSON.stringify(linkdingTags)) { return true; } return false; }, // Get last sync timestamp getLastSyncTime() { return new Date().toISOString(); }, // Format sync timestamp for display formatTimestamp(timestamp) { if (!timestamp) return 'Never'; const date = new Date(timestamp); const options = { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', timeZoneName: 'short' }; return date.toLocaleDateString('en-US', options); } }; // Export for use in other scripts window.SyncUtils = SyncUtils; })();