fix(sync): If `baseVersionEtag` changed, reset frontend

`baseVersionEtag` changes when a new document session got initialized,
e.g. after an old document session without session clients got cleaned
up, or because the markdown file got changed via webdav.

Detect this in the client and ask the user to reload the page for
resetting the session.

Signed-off-by: Jonas <jonas@freesources.org>
This commit is contained in:
Jonas 2024-03-15 11:53:24 +01:00
Родитель 4381329b65
Коммит 670d2f8cad
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 5262E7FF491049FE
6 изменённых файлов: 15 добавлений и 8 удалений

Просмотреть файл

@ -80,8 +80,8 @@ class PublicSessionController extends PublicShareController implements ISessionA
#[NoAdminRequired]
#[PublicPage]
public function create(string $token, ?string $file = null, ?string $guestName = null): DataResponse {
return $this->apiService->create(null, $file, $token, $guestName);
public function create(string $token, ?string $file = null, ?string $baseVersionEtag = null, ?string $guestName = null): DataResponse {
return $this->apiService->create(null, $file, $baseVersionEtag, $token, $guestName);
}
#[NoAdminRequired]

Просмотреть файл

@ -57,8 +57,8 @@ class SessionController extends ApiController implements ISessionAwareController
}
#[NoAdminRequired]
public function create(?int $fileId = null, ?string $file = null): DataResponse {
return $this->apiService->create($fileId, $file, null, null);
public function create(?int $fileId = null, ?string $file = null, ?string $baseVersionEtag = null): DataResponse {
return $this->apiService->create($fileId, $file, $baseVersionEtag, null, null);
}
#[NoAdminRequired]

Просмотреть файл

@ -71,7 +71,7 @@ class ApiService {
$this->l10n = $l10n;
}
public function create(?int $fileId = null, ?string $filePath = null, ?string $token = null, ?string $guestName = null): DataResponse {
public function create(?int $fileId = null, ?string $filePath = null, ?string $baseVersionEtag = null, ?string $token = null, ?string $guestName = null): DataResponse {
try {
if ($token) {
$file = $this->documentService->getFileByShareToken($token, $this->request->getParam('filePath'));
@ -115,6 +115,9 @@ class ApiService {
$this->sessionService->removeInactiveSessionsWithoutSteps($file->getId());
$document = $this->documentService->getDocument($file->getId());
$freshSession = $document === null;
if ($baseVersionEtag && $baseVersionEtag !== $document?->getBaseVersionEtag()) {
return new DataResponse($this->l10n->t('Editing session has expired. Please reload the page.'), Http::STATUS_CONFLICT);
}
if ($freshSession) {
$this->logger->info('Create new document of ' . $file->getId());

Просмотреть файл

@ -374,6 +374,7 @@ export default {
guestName,
shareToken: this.shareToken,
filePath: this.relativePath,
baseVersionEtag: this.$syncService?.baseVersionEtag,
forceRecreate: this.forceRecreate,
serialize: this.isRichEditor
? (content) => createMarkdownSerializer(this.$editor.schema).serialize(content ?? this.$editor.state.doc)

Просмотреть файл

@ -38,9 +38,10 @@ class SessionApi {
this.#options = options
}
open({ fileId }) {
open({ fileId, baseVersionEtag }) {
return axios.put(this.#url(`session/${fileId}/create`), {
fileId,
baseVersionEtag,
filePath: this.#options.filePath,
token: this.#options.shareToken,
guestName: this.#options.guestName,

Просмотреть файл

@ -68,7 +68,7 @@ class SyncService {
#sendIntervalId
constructor({ serialize, getDocumentState, ...options }) {
constructor({ baseVersionEtag, serialize, getDocumentState, ...options }) {
/** @type {import('mitt').Emitter<import('./SyncService').EventTypes>} _bus */
this._bus = mitt()
@ -82,6 +82,7 @@ class SyncService {
this.lastStepPush = Date.now()
this.version = null
this.baseVersionEtag = baseVersionEtag
this.sending = false
this.#sendIntervalId = null
@ -94,7 +95,7 @@ class SyncService {
const connect = initialSession
? Promise.resolve(new Connection({ data: initialSession }, {}))
: this._api.open({ fileId })
: this._api.open({ fileId, baseVersionEtag: this.baseVersionEtag })
.catch(error => this._emitError(error))
this.connection = await connect
@ -104,6 +105,7 @@ class SyncService {
}
this.backend = new PollingBackend(this, this.connection)
this.version = this.connection.docStateVersion
this.baseVersionEtag = this.connection.document.baseVersionEtag
this.emit('opened', {
...this.connection.state,
version: this.version,