-
Notifications
You must be signed in to change notification settings - Fork 120
Expand file tree
/
Copy pathSaveService.ts
More file actions
120 lines (107 loc) · 2.96 KB
/
SaveService.ts
File metadata and controls
120 lines (107 loc) · 2.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import debounce from 'debounce'
import type { ShallowRef } from 'vue'
import { save, saveViaSendBeacon } from '../apis/save'
import type { Connection } from '../composables/useConnection.ts'
import { logger } from '../helpers/logger.js'
import type { SyncService } from './SyncService.js'
/**
* Interval to save the serialized document and the document state
*
* @type {number} time in ms
*/
const AUTOSAVE_INTERVAL = 30000
class SaveService {
connection: ShallowRef<Connection | undefined>
syncService
serialize
getDocumentState
hasYjsPendingStructs
autosave
constructor({
connection,
syncService,
serialize,
getDocumentState,
hasYjsPendingStructs,
}: {
connection: ShallowRef<Connection | undefined>
syncService: SyncService
serialize: () => string
getDocumentState: () => string
hasYjsPendingStructs: () => boolean
}) {
this.connection = connection
this.syncService = syncService
this.serialize = serialize
this.getDocumentState = getDocumentState
this.hasYjsPendingStructs = hasYjsPendingStructs
this.autosave = debounce(this._autosave.bind(this), AUTOSAVE_INTERVAL)
this.syncService.on('close', () => {
this.autosave.clear()
})
}
get version() {
return this.syncService.version
}
get emit() {
return this.syncService.emit.bind(this.syncService)
}
_getContent() {
return this.serialize()
}
async save({ force = false, manualSave = true } = {}) {
logger.debug('[SaveService] saving', { force, manualSave })
if (!this.connection.value) {
logger.warn('Could not save due to missing connection')
return
}
// Don't save if we have pendingStructs (i.e. document state misses a step)
if (this.hasYjsPendingStructs()) {
logger.debug('[SaveService] not saving due to inconsistent yjs state')
return
}
try {
const response = await save(this.connection.value, {
version: this.version,
autosaveContent: this._getContent(),
documentState: this.getDocumentState(),
force,
manualSave,
})
this.emit('stateChange', { dirty: false })
logger.debug('[SaveService] saved', { response })
this.emit('save', response.data)
this.autosave.clear()
} catch (e) {
logger.error('Failed to save document.', { error: e })
throw e
}
}
saveViaSendBeacon() {
if (!this.connection.value) {
return
}
// Don't save if we have pendingStructs (i.e. document state misses a step)
if (this.hasYjsPendingStructs()) {
return
}
saveViaSendBeacon(this.connection.value, {
version: this.version,
autosaveContent: this._getContent(),
documentState: this.getDocumentState(),
}) && logger.debug('[SaveService] saved using sendBeacon')
}
forceSave() {
return this.save({ force: true })
}
_autosave() {
return this.save({ manualSave: false }).catch((error) => {
logger.error('Failed to autosave document.', { error })
})
}
}
export { SaveService }