FEATURE: Check if draft exists before starting a new one (#6755)

Co-Authored-By: Bianca Nenciu <nbianca@users.noreply.github.com>
Co-Authored-By: zogstrip <regis@hanol.fr>
This commit is contained in:
Joffrey JAFFEUX
2018-12-12 10:21:51 +01:00
committed by GitHub
parent 0ca61242b8
commit 3a799ed922
5 changed files with 159 additions and 58 deletions

View File

@ -62,6 +62,12 @@ function loadDraft(store, opts) {
const _popupMenuOptionsCallbacks = []; const _popupMenuOptionsCallbacks = [];
let _checkDraftPopup = !Ember.testing;
export function toggleCheckDraftPopup(enabled) {
_checkDraftPopup = enabled;
}
export function clearPopupMenuOptionsCallback() { export function clearPopupMenuOptionsCallback() {
_popupMenuOptionsCallbacks.length = 0; _popupMenuOptionsCallbacks.length = 0;
} }
@ -770,23 +776,30 @@ export default Ember.Controller.extend({
.then(resolve, reject); .then(resolve, reject);
} }
// we need a draft sequence for the composer to work
if (opts.draftSequence === undefined) {
return Draft.get(opts.draftKey)
.then(function(data) {
opts.draftSequence = data.draft_sequence;
opts.draft = data.draft;
self._setModel(composerModel, opts);
})
.then(resolve, reject);
}
if (composerModel) { if (composerModel) {
if (composerModel.get("action") !== opts.action) { if (composerModel.get("action") !== opts.action) {
composerModel.setProperties({ unlistTopic: false, whisper: false }); composerModel.setProperties({ unlistTopic: false, whisper: false });
} }
} }
// check if there is another draft saved on server
// or get a draft sequence number
if (!opts.draft || opts.draftSequence === undefined) {
return Draft.get(opts.draftKey)
.then(data => self.confirmDraftAbandon(data))
.then(data => {
opts.draft = opts.draft || data.draft;
// we need a draft sequence for the composer to work
if (opts.draft_sequence === undefined) {
opts.draftSequence = data.draft_sequence;
}
self._setModel(composerModel, opts);
})
.then(resolve, reject);
}
self._setModel(composerModel, opts); self._setModel(composerModel, opts);
resolve(); resolve();
}); });
@ -865,6 +878,41 @@ export default Ember.Controller.extend({
} }
}, },
confirmDraftAbandon(data) {
if (!data.draft) {
return data;
}
// do not show abandon dialog if old draft is clean
const draft = JSON.parse(data.draft);
if (draft.reply === draft.originalText) {
data.draft = null;
return data;
}
if (_checkDraftPopup) {
return new Ember.RSVP.Promise(resolve => {
bootbox.dialog(I18n.t("drafts.abandon.confirm"), [
{
label: I18n.t("drafts.abandon.no_value"),
callback: () => resolve(data)
},
{
label: I18n.t("drafts.abandon.yes_value"),
class: "btn-danger",
callback: () => {
data.draft = null;
resolve(data);
}
}
]);
});
} else {
data.draft = null;
return data;
}
},
cancelComposer() { cancelComposer() {
return new Ember.RSVP.Promise(resolve => { return new Ember.RSVP.Promise(resolve => {
if (this.get("model.hasMetaData") || this.get("model.replyDirty")) { if (this.get("model.hasMetaData") || this.get("model.replyDirty")) {

View File

@ -296,6 +296,10 @@ en:
new_topic: "New topic draft" new_topic: "New topic draft"
new_private_message: "New private message draft" new_private_message: "New private message draft"
topic_reply: "Draft reply" topic_reply: "Draft reply"
abandon:
confirm: "You already opened another draft in this topic. Are you sure you want to abandon it?"
yes_value: "Yes, abandon"
no_value: "No, keep"
topic_count_latest: topic_count_latest:
one: "See {{count}} new or updated topic" one: "See {{count}} new or updated topic"

View File

@ -1,7 +1,16 @@
import { acceptance, replaceCurrentUser } from "helpers/qunit-helpers"; import { acceptance } from "helpers/qunit-helpers";
import { toggleCheckDraftPopup } from "discourse/controllers/composer";
acceptance("Composer", { acceptance("Composer", {
loggedIn: true, loggedIn: true,
pretend(server, helper) {
server.get("/draft.json", () => {
return helper.response({
draft: null,
draft_sequence: 42
});
});
},
settings: { settings: {
enable_whispers: true enable_whispers: true
} }
@ -527,49 +536,25 @@ QUnit.test(
} }
); );
acceptance("Composer and uncategorized is not allowed", { QUnit.test("Checks for existing draft", async assert => {
loggedIn: true, toggleCheckDraftPopup(true);
settings: {
enable_whispers: true, // prettier-ignore
allow_uncategorized_topics: false server.get("/draft.json", () => { // eslint-disable-line no-undef
} return [ 200, { "Content-Type": "application/json" }, {
}); draft: "{\"reply\":\"This is a draft of the first post\",\"action\":\"reply\",\"categoryId\":1,\"archetypeId\":\"regular\",\"metaData\":null,\"composerTime\":2863,\"typingTime\":200}",
draft_sequence: 42
QUnit.test("Disable body until category is selected", async assert => { } ];
replaceCurrentUser({ admin: false, staff: false, trust_level: 1 }); });
await visit("/"); await visit("/t/internationalization-localization/280");
await click("#create-topic");
assert.ok(exists(".d-editor-input"), "the composer input is visible"); await click(".topic-post:eq(0) button.show-more-actions");
assert.ok( await click(".topic-post:eq(0) button.edit");
exists(".title-input .popup-tip.bad.hide"),
"title errors are hidden by default" assert.equal(find(".modal-body").text(), I18n.t("drafts.abandon.confirm"));
);
assert.ok( await click(".modal-footer .btn.btn-default");
exists(".d-editor-textarea-wrapper .popup-tip.bad.hide"),
"body errors are hidden by default" toggleCheckDraftPopup(false);
);
assert.ok(
exists(".d-editor-textarea-wrapper.disabled"),
"textarea is disabled"
);
const categoryChooser = selectKit(".category-chooser");
await categoryChooser.expand();
await categoryChooser.selectRowByValue(2);
assert.ok(
find(".d-editor-textarea-wrapper.disabled").length === 0,
"textarea is enabled"
);
await fillIn(".d-editor-input", "Now I can type stuff");
await categoryChooser.expand();
await categoryChooser.selectRowByValue("__none__");
assert.ok(
find(".d-editor-textarea-wrapper.disabled").length === 0,
"textarea is still enabled"
);
}); });

View File

@ -0,0 +1,56 @@
import { acceptance, replaceCurrentUser } from "helpers/qunit-helpers";
acceptance("Composer and uncategorized is not allowed", {
loggedIn: true,
pretend(server, helper) {
server.get("/draft.json", () => {
return helper.response({
draft: null,
draft_sequence: 42
});
});
},
settings: {
enable_whispers: true,
allow_uncategorized_topics: false
}
});
QUnit.test("Disable body until category is selected", async assert => {
replaceCurrentUser({ admin: false, staff: false, trust_level: 1 });
await visit("/");
await click("#create-topic");
assert.ok(exists(".d-editor-input"), "the composer input is visible");
assert.ok(
exists(".title-input .popup-tip.bad.hide"),
"title errors are hidden by default"
);
assert.ok(
exists(".d-editor-textarea-wrapper .popup-tip.bad.hide"),
"body errors are hidden by default"
);
assert.ok(
exists(".d-editor-textarea-wrapper.disabled"),
"textarea is disabled"
);
const categoryChooser = selectKit(".category-chooser");
await categoryChooser.expand();
await categoryChooser.selectRowByValue(2);
assert.ok(
find(".d-editor-textarea-wrapper.disabled").length === 0,
"textarea is enabled"
);
await fillIn(".d-editor-input", "Now I can type stuff");
await categoryChooser.expand();
await categoryChooser.selectRowByValue("__none__");
assert.ok(
find(".d-editor-textarea-wrapper.disabled").length === 0,
"textarea is still enabled"
);
});

View File

@ -41,10 +41,18 @@ QUnit.test("Viewing Summary", async assert => {
}); });
QUnit.test("Viewing Drafts", async assert => { QUnit.test("Viewing Drafts", async assert => {
// prettier-ignore
server.get("/draft.json", () => { // eslint-disable-line no-undef
return [ 200, { "Content-Type": "application/json" }, {
draft: "{\"reply\":\"This is a draft of the first post\",\"action\":\"reply\",\"categoryId\":1,\"archetypeId\":\"regular\",\"metaData\":null,\"composerTime\":2863,\"typingTime\":200}",
draft_sequence: 42
} ];
});
await visit("/u/eviltrout/activity/drafts"); await visit("/u/eviltrout/activity/drafts");
assert.ok(exists(".user-stream"), "has drafts stream"); assert.ok(exists(".user-stream"), "has drafts stream");
assert.ok( assert.ok(
$(".user-stream .user-stream-item-draft-actions").length, exists(".user-stream .user-stream-item-draft-actions"),
"has draft action buttons" "has draft action buttons"
); );