function saveRevision(source) { console.log('Syncing started from: ' + source); chrome.bookmarks.getTree(async (tree) => { const apiUrl = await chrome.storage.sync.get('apiUrl'); const syncToken = await chrome.storage.sync.get("syncToken"); if (!apiUrl || !syncToken) return; const savedRev = await chrome.storage.sync.get('revId'); const url = apiUrl.apiUrl + '/saveRevision'; const options = { headers: { 'content-type': 'application/json', 'syncToken': syncToken.syncToken }, method: 'POST', body: JSON.stringify({ 'revId': savedRev.revId, 'bookmarks': tree }) }; try { const response = await fetch(url, options); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const data = await response.json(); // Convert to JSON chrome.storage.sync.set({ revId: data.revId }); } catch (e) { // TODO GD: Indicate error } }); } async function getRevision() { const apiUrl = await chrome.storage.sync.get('apiUrl'); const syncToken = await chrome.storage.sync.get("syncToken"); if (!apiUrl || !syncToken) return; const url = apiUrl.apiUrl + '/getRevision'; const options = { headers: { 'content-type': 'application/json', 'syncToken': syncToken.syncToken }, method: 'GET' }; try { const response = await fetch(url, options); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const data = await response.json(); // Convert to JSON const savedRev = await chrome.storage.sync.get('revId'); if (data.revId === savedRev.revId) return; chrome.storage.sync.set({ revId: data.revId }); removeAllChildren("1", () => createBookmarkTree(data.bookmarks, "1")); removeAllChildren("2", () => createBookmarkTree(data.bookmarks, "2")); } catch (e) { // TODO GD: Indicate error } } // Recursively delete all children function removeAllChildren(parentId, callback) { chrome.bookmarks.getChildren(parentId, (children) => { if (!children || children.length === 0) return callback?.(); let count = children.length; children.forEach((child) => { if (child.url) { chrome.bookmarks.remove(child.id, () => { if (--count === 0) callback?.(); }); } else { removeAllChildren(child.id, () => { chrome.bookmarks.remove(child.id, () => { if (--count === 0) callback?.(); }); }); } }); }); } // Sync events chrome.bookmarks.onCreated.addListener((id, bookmark) => { saveRevision('onCreated'); }); chrome.bookmarks.onRemoved.addListener((id, removeInfo) => { saveRevision('onRemoved'); }); chrome.bookmarks.onChanged.addListener((id, changeInfo) => { saveRevision('onChanged'); }); chrome.bookmarks.onMoved.addListener((id, moveInfo) => { saveRevision('onMoved'); }); chrome.bookmarks.onChildrenReordered.addListener((id, reorderInfo) => { saveRevision('onChildrenReordered'); }); chrome.bookmarks.onImportEnded.addListener(() => { saveRevision('onImportEnded'); }); getRevision(); setInterval(getRevision, 5000);