зеркало из https://github.com/nextcloud/text.git
Disconnect session after 30 minutes of idle timeout
Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
Родитель
58c3491460
Коммит
08f8725db8
|
@ -22,12 +22,15 @@
|
|||
|
||||
<template>
|
||||
<div id="editor-container">
|
||||
<div v-if="currentSession && active">
|
||||
<div v-if="currentSession && active" class="document-status">
|
||||
<p v-if="idle" class="msg icon-info">
|
||||
{{ t('text', 'Document idle for {timeout} minutes, click to continue editing', { timeout: IDLE_TIMEOUT }) }} <a class="button primary" @click="reconnect">{{ t('text', 'Reconnect') }}</a>
|
||||
</p>
|
||||
<p v-if="hasSyncCollission" class="msg icon-error">
|
||||
{{ t('text', 'The document has been changed outside of the editor. The changes cannot be applied.') }}
|
||||
</p>
|
||||
<p v-if="hasConnectionIssue" class="msg icon-info">
|
||||
{{ t('text', 'File could not be loaded. Please check your internet connection.') }} <a class="button primary" @click="reconnect">{{ t('text', 'Retry') }}</a>
|
||||
{{ t('text', 'File could not be loaded. Please check your internet connection.') }} <a class="button primary" @click="reconnect">{{ t('text', 'Reconnect') }}</a>
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="currentSession && active" id="editor-wrapper" :class="{'has-conflicts': hasSyncCollission, 'icon-loading': !initialLoading || hasConnectionIssue, 'richEditor': isRichEditor}">
|
||||
|
@ -72,7 +75,7 @@ import Vue from 'vue'
|
|||
import escapeHtml from 'escape-html'
|
||||
import moment from '@nextcloud/moment'
|
||||
|
||||
import { SyncService, ERROR_TYPE } from './../services/SyncService'
|
||||
import { SyncService, ERROR_TYPE, IDLE_TIMEOUT } from './../services/SyncService'
|
||||
import { endpointUrl, getRandomGuestName } from './../helpers'
|
||||
import { extensionHighlight } from '../helpers/mappings'
|
||||
import { createEditor, markdownit, createMarkdownSerializer, serializePlainText, loadSyntaxHighlight } from './../EditorFactory'
|
||||
|
@ -143,6 +146,8 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
IDLE_TIMEOUT,
|
||||
|
||||
tiptap: null,
|
||||
/** @type SyncService */
|
||||
syncService: null,
|
||||
|
@ -153,6 +158,7 @@ export default {
|
|||
|
||||
filteredSessions: {},
|
||||
|
||||
idle: false,
|
||||
dirty: false,
|
||||
initialLoading: false,
|
||||
lastSavedString: '',
|
||||
|
@ -393,6 +399,12 @@ export default {
|
|||
this.dirty = state.dirty
|
||||
}
|
||||
})
|
||||
.on('idle', () => {
|
||||
this.syncService.close()
|
||||
this.idle = true
|
||||
this.readOnly = true
|
||||
this.tiptap.setOptions({ editable: !this.readOnly })
|
||||
})
|
||||
if (this.initialSession === null) {
|
||||
this.syncService.open({
|
||||
fileId: this.fileId,
|
||||
|
@ -421,6 +433,7 @@ export default {
|
|||
},
|
||||
|
||||
reconnect() {
|
||||
this.initialLoading = true
|
||||
if (this.syncService) {
|
||||
this.syncService.close().then(() => {
|
||||
this.syncService = null
|
||||
|
@ -434,6 +447,7 @@ export default {
|
|||
this.tiptap.destroy()
|
||||
this.initSession()
|
||||
}
|
||||
this.idle = false
|
||||
},
|
||||
|
||||
updateSessions(sessions) {
|
||||
|
@ -508,10 +522,10 @@ export default {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.msg.icon-error {
|
||||
.document-status .msg {
|
||||
padding: 12px;
|
||||
border-bottom:1px solid var(--color-border);
|
||||
padding-left: 30px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
background-position: 8px center;
|
||||
}
|
||||
|
||||
|
|
|
@ -125,6 +125,9 @@ class PollingBackend {
|
|||
this._authority.sessions = response.data.sessions
|
||||
|
||||
if (response.data.steps.length === 0) {
|
||||
if (this._authority.checkIdle()) {
|
||||
return
|
||||
}
|
||||
this.lock = false
|
||||
if (response.data.sessions.filter((session) => session.lastContact > Date.now() / 1000 - COLLABORATOR_DISCONNECT_TIME).length < 2) {
|
||||
this.maximumRefetchTimer()
|
||||
|
|
|
@ -31,6 +31,13 @@ const defaultOptions = {
|
|||
serialize: (document) => document,
|
||||
}
|
||||
|
||||
/**
|
||||
* Timeout after which the editor will consider a document without changes being synced as idle
|
||||
* The session will be terminated and the document will stay open in read-only mode with a button to reconnect if needed
|
||||
* @type {number}
|
||||
*/
|
||||
const IDLE_TIMEOUT = 30
|
||||
|
||||
const ERROR_TYPE = {
|
||||
/**
|
||||
* Failed to save collaborative document due to external change
|
||||
|
@ -68,6 +75,8 @@ class SyncService {
|
|||
change: [],
|
||||
/* Emitted after successful save */
|
||||
save: [],
|
||||
/* Emitted once a document becomes idle */
|
||||
idle: [],
|
||||
}
|
||||
|
||||
this.backend = new PollingBackend(this)
|
||||
|
@ -81,6 +90,8 @@ class SyncService {
|
|||
this.steps = []
|
||||
this.stepClientIDs = []
|
||||
|
||||
this.lastStepPush = Date.now()
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
|
@ -202,10 +213,19 @@ class SyncService {
|
|||
})
|
||||
})
|
||||
}
|
||||
this.lastStepPush = Date.now()
|
||||
this.emit('sync', { steps: newSteps, document })
|
||||
console.debug('receivedSteps', 'newVersion', this._getVersion())
|
||||
}
|
||||
|
||||
checkIdle() {
|
||||
const lastPushMinutesAgo = (Date.now() - this.lastStepPush) / 1000 / 60
|
||||
if (lastPushMinutesAgo > IDLE_TIMEOUT) {
|
||||
console.debug(`[SyncService] Document is idle for ${this.IDLE_TIMEOUT} minutes, suspending connection`)
|
||||
this.emit('idle')
|
||||
}
|
||||
}
|
||||
|
||||
_getVersion() {
|
||||
if (this.state) {
|
||||
return getVersion(this.state)
|
||||
|
@ -294,4 +314,4 @@ class SyncService {
|
|||
}
|
||||
|
||||
export default SyncService
|
||||
export { SyncService, ERROR_TYPE }
|
||||
export { SyncService, ERROR_TYPE, IDLE_TIMEOUT }
|
||||
|
|
Загрузка…
Ссылка в новой задаче