Skip to content

Commit 06fa356

Browse files
committed
feat(SessionApi): Send save request via sendBeacon at beforeunload
This will send a final save request on unsaved changes via the browsers native `navigator.sendBeacon()` function when navigating away from the website or the tab/browser is closed. Fixes: #6606 Implementation details: * While `beforeunload` event is less reliable than `visibilitychange` according to different sources on the internet, tests in Firefox on Linux desktop revealed that `visibilitychange` event doesn't fire when opening a new website in the same tab. `beforeunload` on the other hand reliably worked when switching web page and closing tab/browser. * We add and remove the `beforeunload` event with every `dirty` state change because according to MDN documentation websites with a registered `beforeunload` event don't benefit from bfcache optimization. See https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event Signed-off-by: Jonas <jonas@freesources.org>
1 parent 5eda823 commit 06fa356

3 files changed

Lines changed: 44 additions & 8 deletions

File tree

src/components/Editor.vue

Lines changed: 11 additions & 0 deletions
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)) {
@@ -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/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: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,17 @@ class SyncService {
300300
}
301301
}
302302

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+
303314
forceSave() {
304315
return this.save({ force: true })
305316
}

0 commit comments

Comments
 (0)