From 55e46790a9ef8f97bc5539a0a73189be3b7f754e Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 27 Jul 2023 16:19:02 +0200 Subject: [PATCH] fix(sync): prevent duplicate pushes Document changes trigger awareness updates. Wait 50ms before sending them in a push request so they can be combined. Signed-off-by: Max --- src/components/Editor.vue | 9 --------- src/extensions/CollaborationCursor.js | 22 ++++++++++++++-------- src/services/SyncService.js | 6 +----- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/components/Editor.vue b/src/components/Editor.vue index d8a3cec0b1a..ca8c0fd7ca1 100644 --- a/src/components/Editor.vue +++ b/src/components/Editor.vue @@ -321,7 +321,6 @@ export default { }, created() { this.$ydoc = new Doc() - this.$ydoc.on('update', this.onYjsUpdate) this.$providers = [] this.$editor = null this.$syncService = null @@ -508,7 +507,6 @@ export default { : (session?.guestName || t('text', 'Guest')), color: session?.color, clientId: this.$ydoc.clientID, - lastUpdate: Date.now(), }, }), Keymap.configure({ @@ -644,13 +642,6 @@ export default { this.emit('delete-image-node', imageUrl) }, - onYjsUpdate(_update, origin) { - if (origin.key === 'y-sync$') { - // Update timestamp of own cursor - this.$editor.commands.updateSelf() - } - }, - async close() { if (this.currentSession && this.$syncService) { try { diff --git a/src/extensions/CollaborationCursor.js b/src/extensions/CollaborationCursor.js index 927e96074e6..b888955fa87 100644 --- a/src/extensions/CollaborationCursor.js +++ b/src/extensions/CollaborationCursor.js @@ -20,6 +20,13 @@ function showCursorLabel(clientId) { }, 50) } +/** + * Unix timestamp in seconds. + */ +function getTimestamp() { + return Math.floor(Date.now() / 1000) +} + const CollaborationCursor = TiptapCollaborationCursor.extend({ addOptions() { return { @@ -28,7 +35,7 @@ const CollaborationCursor = TiptapCollaborationCursor.extend({ name: null, clientId: null, color: null, - lastUpdate: null, + lastUpdate: getTimestamp(), }, render: user => { const cursor = document.createElement('span') @@ -61,13 +68,12 @@ const CollaborationCursor = TiptapCollaborationCursor.extend({ }) }, - addCommands() { - return { - ...this.parent(), - updateSelf: () => ({ editor }) => { - const attributes = { ...this.options.user, lastUpdate: Date.now() } - return editor.commands.updateUser(attributes) - }, + // Flag own cursor as active on undoable changes to the document state + onTransaction({ transaction }) { + const { updated, meta } = transaction + if (updated && (meta.addToHistory ?? true) && !meta.pointer) { + this.options.user.lastUpdate = getTimestamp() + this.options.provider.awareness.setLocalStateField('user', this.options.user) } }, }) diff --git a/src/services/SyncService.js b/src/services/SyncService.js index a2eb814012d..d482e7fe130 100644 --- a/src/services/SyncService.js +++ b/src/services/SyncService.js @@ -150,14 +150,10 @@ class SyncService { } sendSteps(getSendable) { - // If already retrying, do nothing. + // If already waiting to send, do nothing. if (this.#sendIntervalId) { return } - if (this.connection && !this.sending) { - return this._sendSteps(getSendable) - } - // If already sending, retry every 200ms. return new Promise((resolve, reject) => { this.#sendIntervalId = setInterval(() => { if (this.connection && !this.sending) {