fix: Improved handling of simultaneous edits

This commit is contained in:
Tom Moor
2020-06-02 23:16:15 -07:00
parent 05e24df226
commit 33815639f2
3 changed files with 58 additions and 29 deletions

View File

@ -128,22 +128,6 @@ class SocketProvider extends React.Component<Props> {
});
}
}
// TODO: Move this to the document scene once data loading
// has been refactored to be friendlier there.
if (
auth.user &&
documentId === ui.activeDocumentId &&
document.updatedBy.id !== auth.user.id
) {
ui.showToast(`Document updated by ${document.updatedBy.name}`, {
timeout: 30 * 1000,
action: {
text: 'Refresh',
onClick: () => window.location.reload(),
},
});
}
}
}

View File

@ -10,7 +10,12 @@ import Revision from 'models/Revision';
import User from 'models/User';
import DocumentsStore from 'stores/DocumentsStore';
type SaveOptions = { publish?: boolean, done?: boolean, autosave?: boolean };
type SaveOptions = {
publish?: boolean,
done?: boolean,
autosave?: boolean,
lastRevision?: number,
};
export default class Document extends BaseModel {
@observable isSaving: boolean = false;
@ -185,7 +190,7 @@ export default class Document extends BaseModel {
id: this.id,
title: this.title,
text: this.text,
lastRevision: this.revision,
lastRevision: options.lastRevision,
...options,
});
} finally {

View File

@ -14,6 +14,7 @@ import {
documentMoveUrl,
documentHistoryUrl,
documentEditUrl,
documentUrl,
} from 'utils/routeHelpers';
import { emojiToUrl } from 'utils/emoji';
@ -74,11 +75,13 @@ class DocumentScene extends React.Component<Props> {
@observable isDirty: boolean = false;
@observable isEmpty: boolean = true;
@observable moveModalOpen: boolean = false;
@observable lastRevision: number;
@observable title: string;
constructor(props) {
super();
this.title = props.document.title;
this.lastRevision = props.document.revision;
this.loadEditor();
}
@ -86,6 +89,29 @@ class DocumentScene extends React.Component<Props> {
this.updateIsDirty();
}
componentDidUpdate(prevProps) {
const { auth, document } = this.props;
if (this.props.readOnly) {
this.lastRevision = document.revision;
} else if (prevProps.document.revision !== this.lastRevision) {
if (auth.user && document.updatedBy.id !== auth.user.id) {
this.props.ui.showToast(
`Document updated by ${document.updatedBy.name}`,
{
timeout: 30 * 1000,
action: {
text: 'Reload',
onClick: () => {
window.location.href = documentUrl(document);
},
},
}
);
}
}
}
@keydown('m')
goToMove(ev) {
if (!this.props.readOnly) return;
@ -174,7 +200,11 @@ class DocumentScene extends React.Component<Props> {
handleOpenMoveModal = () => (this.moveModalOpen = true);
onSave = async (
options: { done?: boolean, publish?: boolean, autosave?: boolean } = {}
options: {
done?: boolean,
publish?: boolean,
autosave?: boolean,
} = {}
) => {
const { document } = this.props;
@ -202,10 +232,14 @@ class DocumentScene extends React.Component<Props> {
let isNew = !document.id;
this.isSaving = true;
this.isPublishing = !!options.publish;
const savedDocument = await document.save(options);
try {
const savedDocument = await document.save({
...options,
lastRevision: this.lastRevision,
});
this.isDirty = false;
this.isSaving = false;
this.isPublishing = false;
this.lastRevision = savedDocument.revision;
if (options.done) {
this.props.history.push(savedDocument.url);
@ -214,6 +248,12 @@ class DocumentScene extends React.Component<Props> {
this.props.history.push(documentEditUrl(savedDocument));
this.props.ui.setActiveDocument(savedDocument);
}
} catch (err) {
this.props.ui.showToast(err.message);
} finally {
this.isSaving = false;
this.isPublishing = false;
}
};
autosave = debounce(() => {