FIX: never save draft while it is saving

Previously if saving a draft took longer than 2 seconds there
could be conditions where drafts could be saved concurrently.
This meant the composer could race with itself and raise conflicts.

This is likely to happen on bad internet connections or where
latency is really high.

Additionally a throttle was added so drafts save unconditionally
every 15 seconds.

Save draft in the model now properly and consistently returns a
promise.
This commit is contained in:
Sam Saffron 2020-05-14 15:35:44 +10:00
parent a849bdf0c9
commit ce4b5b56e5
No known key found for this signature in database
GPG Key ID: B9606168D2FFD9F5
2 changed files with 27 additions and 10 deletions

View File

@ -1095,7 +1095,13 @@ export default Controller.extend({
_saveDraft() {
const model = this.model;
if (model) {
model.saveDraft();
if (model.draftSaving) {
debounce(this, this._saveDraft, 2000);
} else {
model.saveDraft().finally(() => {
this._lastDraftSaved = Date.now();
});
}
}
},
@ -1107,7 +1113,15 @@ export default Controller.extend({
!this.skipAutoSave &&
!this.model.disableDrafts
) {
debounce(this, this._saveDraft, 2000);
if (!this._lastDraftSaved) {
// pretend so we get a save unconditionally in 15 secs
this._lastDraftSaved = Date.now();
}
if (Date.now() - this._lastDraftSaved > 15000) {
this._saveDraft();
} else {
debounce(this, this._saveDraft, 2000);
}
}
},

View File

@ -1086,12 +1086,14 @@ const Composer = RestModel.extend({
},
saveDraft() {
if (this.draftSaving) return Promise.resolve();
// Do not save when drafts are disabled
if (this.disableDrafts) return;
if (this.disableDrafts) return Promise.resolve();
if (this.canEditTitle) {
// Save title and/or post body
if (!this.title && !this.reply) return;
if (!this.title && !this.reply) return Promise.resolve();
if (
this.title &&
@ -1099,14 +1101,15 @@ const Composer = RestModel.extend({
this.reply &&
this.replyLength < this.siteSettings.min_post_length
) {
return;
return Promise.resolve();
}
} else {
// Do not save when there is no reply
if (!this.reply) return;
if (!this.reply) return Promise.resolve();
// Do not save when the reply's length is too small
if (this.replyLength < this.siteSettings.min_post_length) return;
if (this.replyLength < this.siteSettings.min_post_length)
return Promise.resolve();
}
this.setProperties({
@ -1138,13 +1141,11 @@ const Composer = RestModel.extend({
}
if (result.conflict_user) {
this.setProperties({
draftSaving: false,
draftStatus: I18n.t("composer.edit_conflict"),
draftConflictUser: result.conflict_user
});
} else {
this.setProperties({
draftSaving: false,
draftSaved: true,
draftConflictUser: null
});
@ -1169,10 +1170,12 @@ const Composer = RestModel.extend({
}
this.setProperties({
draftSaving: false,
draftStatus: draftStatus || I18n.t("composer.drafts_offline"),
draftConflictUser: null
});
})
.finally(() => {
this.set("draftSaving", false);
});
},