Skip to content

Commit 690ba06

Browse files
authored
Merge pull request #7061 from nextcloud/backport/6798/stable30
[stable30] feat(session): Send save request via `sendBeacon` at `beforeunload`
2 parents 590d185 + c6990dc commit 690ba06

4 files changed

Lines changed: 48 additions & 14 deletions

File tree

src/components/Editor.vue

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,13 @@ export default {
327327
this.contentWrapper = this.$refs.contentWrapper
328328
})
329329
},
330+
dirty(val) {
331+
if (val) {
332+
window.addEventListener('beforeunload', this.saveBeforeUnload)
333+
} else {
334+
window.removeEventListener('beforeunload', this.saveBeforeUnload)
335+
}
336+
},
330337
},
331338
mounted() {
332339
if (this.active && (this.hasDocumentParameters)) {
@@ -642,7 +649,7 @@ export default {
642649
this.emit('ready')
643650
}
644651
if (Object.prototype.hasOwnProperty.call(state, 'dirty')) {
645-
// ignore initial loading and other automated changes
652+
// ignore initial loading and other automated changes before first user change
646653
if (this.$editor
647654
&& (this.$editor.can().undo() || this.$editor.can().redo())
648655
) {
@@ -835,6 +842,10 @@ export default {
835842
})
836843
this.translateModal = false
837844
},
845+
846+
saveBeforeUnload() {
847+
this.$syncService?.saveViaSendBeacon()
848+
},
838849
},
839850
}
840851
</script>

src/components/Editor/Status.vue

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,22 +86,19 @@ export default {
8686
return this.dirtyStateIndicator ? t('text', 'Saving …') : t('text', 'Saved')
8787
},
8888
dirtyStateIndicator() {
89-
return this.dirty || this.hasUnsavedChanges
89+
return this.dirty
9090
},
9191
lastSavedStatusTooltip() {
9292
let message = t('text', 'Last saved {lastSave}', { lastSave: this.lastSavedString })
9393
if (this.hasSyncCollission) {
9494
message = t('text', 'The document has been changed outside of the editor. The changes cannot be applied.')
9595
}
96-
if (this.dirty || this.hasUnsavedChanges) {
96+
if (this.dirty) {
9797
message += ' - ' + t('text', 'Unsaved changes')
9898
}
9999
return message
100100
},
101101
102-
hasUnsavedChanges() {
103-
return this.document && this.document.lastSavedVersion < this.document.currentVersion
104-
},
105102
hasSyncCollission() {
106103
return this.syncError && this.syncError.type === ERROR_TYPE.SAVE_COLLISSION
107104
},

src/services/SessionApi.js

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
55
import axios from '@nextcloud/axios'
6+
import { getRequestToken } from '@nextcloud/auth'
67
import { generateUrl } from '@nextcloud/router'
78

89
export class ConnectionClosedError extends Error {
@@ -106,17 +107,30 @@ export class Connection {
106107
})
107108
}
108109

109-
save({ version, autosaveContent, documentState, force, manualSave }) {
110-
return this.#post(this.#url(`session/${this.#document.id}/save`), {
110+
save(data) {
111+
const url = this.#url(`session/${this.#document.id}/save`)
112+
const postData = {
111113
...this.#defaultParams,
112114
filePath: this.#options.filePath,
113115
baseVersionEtag: this.#document.baseVersionEtag,
114-
version,
115-
autosaveContent,
116-
documentState,
117-
force,
118-
manualSave,
119-
})
116+
...data,
117+
}
118+
119+
return this.#post(url, postData)
120+
}
121+
122+
saveViaSendBeacon(data) {
123+
const url = this.#url(`session/${this.#document.id}/save`)
124+
const postData = {
125+
...this.#defaultParams,
126+
filePath: this.#options.filePath,
127+
baseVersionEtag: this.#document.baseVersionEtag,
128+
...data,
129+
requestToken: getRequestToken() ?? '',
130+
}
131+
132+
const blob = new Blob([JSON.stringify(postData)], { type: 'application/json' })
133+
return navigator.sendBeacon(url, blob)
120134
}
121135

122136
push({ steps, version, awareness }) {

src/services/SyncService.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ class SyncService {
218218
this.emit('error', { type: ERROR_TYPE.PUSH_FORBIDDEN, data: {} })
219219
}
220220
// TODO: does response.data ever have a document? maybe for errors?
221+
// TODO: `currentVersion` is always 0 nowadays. Check if this is still needed.
221222
// Only emit conflict event if we have synced until the latest version
222223
if (response.data.document?.currentVersion === this.version) {
223224
this.emit('error', { type: ERROR_TYPE.PUSH_FAILURE, data: {} })
@@ -299,6 +300,17 @@ class SyncService {
299300
}
300301
}
301302

303+
saveViaSendBeacon() {
304+
this.#connection.saveViaSendBeacon({
305+
version: this.version,
306+
autosaveContent: this._getContent(),
307+
documentState: this.getDocumentState(),
308+
force: false,
309+
manualSave: true,
310+
})
311+
logger.debug('[SyncService] saved using sendBeacon')
312+
}
313+
302314
forceSave() {
303315
return this.save({ force: true })
304316
}

0 commit comments

Comments
 (0)