mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-27 08:03:59 +00:00
* Use opInflightSince to measure unsaved doc time * Use performance.now() for lastAcked timestamp * Use inflightOpCreatedAt * Increase MAX_UNSAVED_ALERT_SECONDS to 15 * Increase MAX_UNSAVED_SECONDS to 30 * Increase FATAL_OP_TIMEOUT to 45000 * Convert "Connection lost" modal to a notification GitOrigin-RevId: 2d4233723620fd03ce6d6c5795c48c33c0b2f92c
126 lines
3.6 KiB
TypeScript
126 lines
3.6 KiB
TypeScript
// Migrated from static methods of Document in Document.js
|
|
|
|
import { DocumentContainer } from '@/features/ide-react/editor/document-container'
|
|
import { debugConsole } from '@/utils/debugging'
|
|
import { Socket } from '@/features/ide-react/connection/types/socket'
|
|
import { IdeEventEmitter } from '@/features/ide-react/create-ide-event-emitter'
|
|
import EditorWatchdogManager from '@/features/ide-react/connection/editor-watchdog-manager'
|
|
|
|
export class OpenDocuments {
|
|
private openDocs = new Map<string, DocumentContainer>()
|
|
|
|
// eslint-disable-next-line no-useless-constructor
|
|
constructor(
|
|
private readonly socket: Socket,
|
|
private readonly globalEditorWatchdogManager: EditorWatchdogManager,
|
|
private readonly events: IdeEventEmitter
|
|
) {}
|
|
|
|
getDocument(docId: string) {
|
|
// Try to clean up existing docs before reopening them. If the doc has no
|
|
// buffered ops then it will be deleted by _cleanup() and a new instance
|
|
// of the document created below. This prevents us trying to follow the
|
|
// joinDoc:existing code path on an existing doc that doesn't have any
|
|
// local changes and getting an error if its version is too old.
|
|
if (this.openDocs.has(docId)) {
|
|
debugConsole.log(
|
|
`[getDocument] Cleaning up existing document instance for ${docId}`
|
|
)
|
|
this.openDocs.get(docId)?.cleanUp()
|
|
}
|
|
if (!this.openDocs.has(docId)) {
|
|
debugConsole.log(
|
|
`[getDocument] Creating new document instance for ${docId}`
|
|
)
|
|
this.createDoc(docId)
|
|
} else {
|
|
debugConsole.log(
|
|
`[getDocument] Returning existing document instance for ${docId}`
|
|
)
|
|
}
|
|
return this.openDocs.get(docId)
|
|
}
|
|
|
|
private createDoc(docId: string) {
|
|
const doc = new DocumentContainer(
|
|
docId,
|
|
this.socket,
|
|
this.globalEditorWatchdogManager,
|
|
this.events,
|
|
this.detachDoc.bind(this)
|
|
)
|
|
this.openDocs.set(docId, doc)
|
|
}
|
|
|
|
detachDoc(docId: string, doc: DocumentContainer) {
|
|
if (this.openDocs.get(docId) === doc) {
|
|
debugConsole.log(
|
|
`[detach] Removing document with ID (${docId}) from openDocs`
|
|
)
|
|
this.openDocs.delete(docId)
|
|
} else {
|
|
// It's possible that this instance has error, and the doc has been reloaded.
|
|
// This creates a new instance in Document.openDoc with the same id. We shouldn't
|
|
// clear it because it's not this instance.
|
|
debugConsole.log(
|
|
`[_cleanUp] New instance of (${docId}) created. Not removing`
|
|
)
|
|
}
|
|
}
|
|
|
|
hasUnsavedChanges() {
|
|
for (const doc of this.openDocs.values()) {
|
|
if (doc.hasBufferedOps()) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
flushAll() {
|
|
for (const doc of this.openDocs.values()) {
|
|
doc.flush()
|
|
}
|
|
}
|
|
|
|
unsavedDocs() {
|
|
const docs = []
|
|
for (const doc of this.openDocs.values()) {
|
|
if (!doc.pollSavedStatus()) {
|
|
docs.push(doc)
|
|
}
|
|
}
|
|
return docs
|
|
}
|
|
|
|
async awaitBufferedOps(signal: AbortSignal) {
|
|
if (this.hasUnsavedChanges()) {
|
|
const { promise, resolve } = Promise.withResolvers<void>()
|
|
|
|
let resolved = false
|
|
|
|
const listener = () => {
|
|
if (!this.hasUnsavedChanges()) {
|
|
debugConsole.log('saved')
|
|
window.removeEventListener('doc:saved', listener)
|
|
resolved = true
|
|
resolve()
|
|
}
|
|
}
|
|
|
|
window.addEventListener('doc:saved', listener)
|
|
|
|
signal.addEventListener('abort', () => {
|
|
if (!resolved) {
|
|
debugConsole.log('aborted')
|
|
window.removeEventListener('doc:saved', listener)
|
|
resolve()
|
|
}
|
|
})
|
|
|
|
this.flushAll()
|
|
|
|
await promise
|
|
}
|
|
}
|
|
}
|